Skip to content

Commit a77f03c

Browse files
authored
Merge pull request #1 from Doczilla-APP/improvements
chore: Added testing
2 parents bbd7adc + 579be5d commit a77f03c

14 files changed

Lines changed: 5563 additions & 152 deletions

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
dist

.eslintrc.json

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"parserOptions": {
5+
"ecmaVersion": 2020,
6+
"sourceType": "module"
7+
},
8+
"extends": [
9+
"eslint:recommended",
10+
"plugin:@typescript-eslint/eslint-recommended",
11+
"plugin:@typescript-eslint/recommended"
12+
],
13+
"plugins": [
14+
"@typescript-eslint",
15+
"simple-import-sort"
16+
],
17+
"overrides": [
18+
{
19+
"files": [
20+
"*.ts"
21+
],
22+
"rules": {
23+
"no-unneeded-ternary": [
24+
"error"
25+
],
26+
"no-nested-ternary": [
27+
"error"
28+
],
29+
"multiline-ternary": [
30+
"error",
31+
"always-multiline"
32+
],
33+
"operator-linebreak": [
34+
"error",
35+
"before"
36+
],
37+
"simple-import-sort/imports": [
38+
"warn",
39+
{
40+
"groups": [
41+
[
42+
"^@?\\w"
43+
],
44+
// "type" imports.
45+
[
46+
"^.*\\u0000$"
47+
],
48+
// Absolute imports and other imports such as Vue-style `@/foo`.
49+
// Anything not matched in another group.
50+
[
51+
"^"
52+
],
53+
// Relative imports.
54+
// Anything that starts with a dot.
55+
[
56+
"^\\."
57+
]
58+
]
59+
}
60+
],
61+
"simple-import-sort/exports": "warn",
62+
"object-curly-spacing": [
63+
"error",
64+
"always"
65+
],
66+
"quotes": [
67+
"warn",
68+
"single"
69+
],
70+
"func-style": [
71+
"warn",
72+
"declaration"
73+
]
74+
}
75+
},
76+
{
77+
"files": [
78+
"*.spec.ts",
79+
"*.spec.tsx",
80+
"*.spec.js",
81+
"*.spec.jsx"
82+
],
83+
"env": {
84+
"jest": true
85+
},
86+
"rules": {}
87+
},
88+
{
89+
"files": "*.json",
90+
"parser": "jsonc-eslint-parser",
91+
"rules": {}
92+
}
93+
]
94+
}

.github/workflows/pr.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: "Check PR"
2+
on:
3+
pull_request:
4+
5+
jobs:
6+
build:
7+
name: Build
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- name: Checkout code
12+
uses: actions/checkout@v4
13+
14+
- name: Cache node modules
15+
id: cache
16+
uses: actions/cache@v3
17+
with:
18+
path: |
19+
~/.cache
20+
**/node_modules
21+
key: cache-node-modules-${{ hashFiles('yarn.lock') }}
22+
restore-keys: |
23+
cache-node-modules-
24+
25+
- name: Set up Node
26+
uses: actions/setup-node@v4
27+
with:
28+
node-version: 20
29+
30+
- name: yarn install
31+
if: steps.cache.outputs.cache-hit != 'true'
32+
shell: bash
33+
run: yarn install --immutable
34+
35+
- name: Build
36+
shell: bash
37+
run: yarn build
38+
39+
test:
40+
name: Test (${{ matrix.node }})
41+
needs: [build]
42+
strategy:
43+
matrix:
44+
os:
45+
- "ubuntu-latest"
46+
node:
47+
- "20"
48+
- "18"
49+
runs-on: ${{ matrix.os }}
50+
steps:
51+
- uses: actions/checkout@v2
52+
53+
- name: Setup node
54+
uses: actions/setup-node@v2
55+
with:
56+
node-version: ${{ matrix.node }}
57+
58+
- name: Print Node.js version
59+
run: node -v
60+
61+
- name: Cache node modules
62+
id: cache
63+
uses: actions/cache@v3
64+
with:
65+
path: |
66+
~/.cache
67+
**/node_modules
68+
key: cache-node-modules-${{ hashFiles('yarn.lock') }}
69+
restore-keys: |
70+
cache-node-modules-
71+
72+
- name: Test
73+
run: yarn test

jest.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** @type {import('ts-jest').JestConfigWithTsJest} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node'
5+
}

package.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,30 @@
2929
"module": "./dist/esm/index.js",
3030
"scripts": {
3131
"build": "tsc && tsc -p tsconfig.esm.json",
32-
"generate:sdk": "node ./generate-models.js && yarn build"
32+
"generate:sdk": "node ./generate-models.js && yarn build",
33+
"lint": "eslint --ext .ts .",
34+
"test": "jest"
3335
},
3436
"dependencies": {
3537
"axios": "^1.6.2"
3638
},
3739
"devDependencies": {
40+
"@jest/globals": "^29.7.0",
41+
"@types/jest": "^29.5.11",
3842
"@types/node": "^20.10.4",
43+
"@typescript-eslint/eslint-plugin": "^6.15.0",
44+
"@typescript-eslint/parser": "^6.15.0",
45+
"axios-mock-adapter": "^1.22.0",
46+
"eslint": "8.56.0",
47+
"eslint-plugin-import": "2.29.1",
48+
"eslint-plugin-simple-import-sort": "^10.0.0",
49+
"jest": "^29.7.0",
3950
"openapi-typescript-codegen": "^0.25.0",
51+
"ts-jest": "^29.1.1",
4052
"typescript": "^5.3"
4153
},
42-
"packageManager": "yarn@4.0.2"
54+
"packageManager": "yarn@4.0.2",
55+
"engines": {
56+
"node": ">=18.*"
57+
}
4358
}

src/Doczilla.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import axios, { Axios } from 'axios'
22

3+
import { version } from '../package.json'
34
import { PdfService } from './services/PdfService'
45
import { ScreenshotService } from './services/ScreenshotService'
56
import { WebhookService } from './services/WebhookService'
6-
import { version } from '../package.json'
77

88
interface DoczillaOptions {
99
baseURL?: string
@@ -18,6 +18,10 @@ export default class Doczilla {
1818
public readonly webhook: WebhookService
1919

2020
constructor(token: string, options: DoczillaOptions = {}) {
21+
if (!token) {
22+
throw new Error('No token provided!')
23+
}
24+
2125
this.client = axios.create({
2226
baseURL: options.baseURL || 'https://api.doczilla.app',
2327
headers: {

src/__tests__/Doczilla.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { describe, expect, test } from '@jest/globals'
2+
3+
import Doczilla from '../Doczilla'
4+
5+
describe('Doczilla', () => {
6+
7+
test('it should throw an error if no api key is provided', () => {
8+
let error: Error
9+
try {
10+
// @ts-expect-error
11+
new Doczilla()
12+
} catch (err) {
13+
error = err
14+
}
15+
16+
expect(error.message).toEqual('No token provided!')
17+
})
18+
19+
})

src/__tests__/page.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { describe, expect, test } from '@jest/globals'
2+
import MockAdapter from 'axios-mock-adapter'
3+
4+
import Doczilla from '../Doczilla'
5+
6+
describe('Page', () => {
7+
8+
const client = new Doczilla('fake-api-token')
9+
// @ts-expect-error private property
10+
const axiosMock = new MockAdapter(client.client)
11+
12+
axiosMock.onAny().reply(200, Buffer.from(''))
13+
14+
test('it encode the page.html option', async () => {
15+
await client.pdf.direct({
16+
page: {
17+
html: '<div>Your first Doczilla PDF</div>'
18+
}
19+
})
20+
21+
expect(axiosMock.history.post.length).toBe(1)
22+
expect(axiosMock.history.post[0].data).toEqual(JSON.stringify({
23+
page: {
24+
html: 'PGRpdj5Zb3VyIGZpcnN0IERvY3ppbGxhIFBERjwvZGl2Pg=='
25+
}
26+
}))
27+
})
28+
29+
})

src/__tests__/pdf.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { describe, expect, test } from '@jest/globals'
2+
import MockAdapter from 'axios-mock-adapter'
3+
4+
import Doczilla from '../Doczilla'
5+
6+
describe('PDF', () => {
7+
8+
const client = new Doczilla('fake-api-token')
9+
// @ts-expect-error private property
10+
const axiosMock = new MockAdapter(client.client)
11+
12+
axiosMock.onAny().reply(200, Buffer.from(''))
13+
14+
test('it should encode the pdf.headerTemplate and pdf.footerTemplate options', async () => {
15+
await client.pdf.direct({
16+
page: {
17+
html: '<div>Your first Doczilla PDF</div>'
18+
},
19+
pdf: {
20+
headerTemplate: '<div>Header template</div>',
21+
footerTemplate: '<div>Footer template</div>'
22+
}
23+
})
24+
25+
expect(axiosMock.history.post.length).toBe(1)
26+
expect(axiosMock.history.post[0].data).toEqual(JSON.stringify({
27+
page: {
28+
html: 'PGRpdj5Zb3VyIGZpcnN0IERvY3ppbGxhIFBERjwvZGl2Pg=='
29+
},
30+
pdf: {
31+
headerTemplate: 'PGRpdj5IZWFkZXIgdGVtcGxhdGU8L2Rpdj4=',
32+
footerTemplate: 'PGRpdj5Gb290ZXIgdGVtcGxhdGU8L2Rpdj4='
33+
}
34+
}))
35+
})
36+
37+
})

src/services/BaseService.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { Axios, AxiosHeaders, AxiosRequestConfig, isAxiosError } from 'axios'
22

3+
import type { AsyncPdf, AsyncScreenshot, CreatePdf, CreateScreenshot, SyncPdf, SyncScreenshot } from '../generated'
4+
5+
type PdfRequests = CreatePdf | SyncPdf | AsyncPdf
6+
type ScreenshotRequests = CreateScreenshot | SyncScreenshot | AsyncScreenshot
7+
type RequestBody = PdfRequests | ScreenshotRequests
8+
39
export class BaseService {
410

511
private rateLimit = {
@@ -16,11 +22,11 @@ export class BaseService {
1622

1723
constructor(private readonly client: Axios) {}
1824

19-
protected async post<T>(url: string, requestBody: object, config: AxiosRequestConfig = {}, retries = 1): Promise<T> {
25+
protected async post<T>(url: string, requestBody: RequestBody, config: AxiosRequestConfig = {}, retries = 1): Promise<T> {
2026
try {
2127
await this.waitForRateLimit()
2228

23-
const axiosResponse = await this.client.post<T>(url, requestBody, config)
29+
const axiosResponse = await this.client.post<T>(url, this.encodeRequestBody(requestBody), config)
2430
this.processRateLimit(new AxiosHeaders(axiosResponse.headers))
2531

2632
if (config.responseType === 'arraybuffer') {
@@ -37,6 +43,22 @@ export class BaseService {
3743
}
3844
}
3945

46+
private encodeRequestBody(requestBody: PdfRequests): object {
47+
if (requestBody.page.html) {
48+
requestBody.page.html = this.baseEncodeContent(requestBody.page.html)
49+
}
50+
51+
if (requestBody.pdf?.headerTemplate) {
52+
requestBody.pdf.headerTemplate = this.baseEncodeContent(requestBody.pdf.headerTemplate)
53+
}
54+
55+
if (requestBody.pdf?.footerTemplate) {
56+
requestBody.pdf.footerTemplate = this.baseEncodeContent(requestBody.pdf.footerTemplate)
57+
}
58+
59+
return requestBody
60+
}
61+
4062
private async waitForRateLimit(): Promise<void> {
4163
// Minus 1 to be safe
4264
if ((this.rateLimit.remaining - 1) <= 0) {
@@ -56,4 +78,7 @@ export class BaseService {
5678
}
5779
}
5880

81+
private baseEncodeContent(content: string): string {
82+
return Buffer.from(content).toString('base64')
83+
}
5984
}

0 commit comments

Comments
 (0)