Skip to content

Commit cb37b23

Browse files
committed
initial deno rework
1 parent 6caab7a commit cb37b23

33 files changed

+3084
-10719
lines changed

.editorconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
trim_trailing_whitespace = true
7+
insert_final_newline = true
8+
9+
[*.md]
10+
insert_final_newline = false
11+
trim_trailing_whitespace = false
12+
13+
[*.{js,json,ts,mts,yml,yaml}]
14+
indent_size = 2
15+
indent_style = space

.eslintignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

.eslintrc.json

Lines changed: 0 additions & 23 deletions
This file was deleted.

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
node_modules
2-
coverage
2+
coverage
3+
scripts
4+
npm
5+
dev
6+
cov_profile
7+
.vscode
8+
.history

.prettierignore

Lines changed: 0 additions & 3 deletions
This file was deleted.

.prettierrc

Lines changed: 0 additions & 14 deletions
This file was deleted.

README.md

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,19 @@ new ScrapeConfig({
7070

7171
## Development
7272

73-
Install and setup environment:
74-
75-
```shell
76-
$ npm install
77-
```
78-
79-
Build and test:
80-
81-
```shell
82-
$ npm run build
83-
$ npm run test
84-
```
73+
This is a Deno Typescript project that builds to NPM through [DNT](https://github.com/denoland/dnt).
74+
75+
- `/src` directory contains all of the source code with `main.ts` being the entry point.
76+
- `__tests__` directory contains tests for the source code.
77+
- `deno.json` contains meta information
78+
- `build.ts` is the build script that builds the project to nodejs ESM package.
79+
- `/npm` directory will be produced when `buil.ts` is executed for building node package.
80+
81+
```bash
82+
# make modifications and run tests
83+
$ deno task test
84+
# format
85+
$ deno fmt
86+
# lint
87+
$ deno linst
88+
```
Lines changed: 92 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,111 @@
1-
import * as errors from '../../src/errors.js';
2-
import { ScrapflyClient } from '../../src/client.js';
3-
import { ExtractionConfig } from '../../src/extractionconfig.js'
4-
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
5-
import { responseFactory } from '../utils.js';
1+
import * as errors from '../../src/errors.ts';
2+
import { ScrapflyClient } from '../../src/client.ts';
3+
import { ExtractionConfig } from '../../src/extractionconfig.ts';
4+
import { assertEquals, assertRejects } from "https://deno.land/std@0.224.0/assert/mod.ts";
5+
import { stub } from "https://deno.land/std/testing/mock.ts";
6+
import { responseFactory } from '../utils.ts';
67

7-
describe('extract', () => {
8+
Deno.test('extract: succeeds', async () => {
89
const KEY = '__API_KEY__';
910
const client = new ScrapflyClient({ key: KEY });
10-
11-
beforeEach(() => {
12-
jest.spyOn(client, 'fetch').mockClear(); // clear all mock meta on each test
11+
const html = 'very long html file';
12+
const fetchStub = stub(client, 'fetch', async (config: Request): Promise<Response> => {
13+
const configUrl = new URL(config.url);
14+
const configBody = await new Response(config.body).text();
15+
assertEquals(configUrl.origin + configUrl.pathname, client.HOST + '/extraction');
16+
assertEquals(config.method, 'POST');
17+
assertEquals(configUrl.searchParams.get('key'), KEY);
18+
assertEquals(configBody, html);
19+
const body = { data: 'a document summary', content_type: 'text/html' };
20+
return responseFactory(body, { status: 200 });
1321
});
1422

15-
it('succeeds', async () => {
16-
const spy = jest.spyOn(client, 'fetch');
17-
const html = 'very long html file';
18-
jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise<any> => {
19-
const configUrl = config[Object.getOwnPropertySymbols(config)[1]].url;
20-
const configBody = config[Object.getOwnPropertySymbols(config)[1]].body.source;
21-
// Ensure the URL matches the pattern
22-
expect(configUrl.origin + configUrl.pathname).toEqual(client.HOST + '/extraction');
23-
expect(config.method).toEqual('POST');
24-
expect(configUrl.searchParams.get('key')).toMatch(KEY);
25-
expect(configBody).toEqual(html);
26-
const body = { data: 'a document summary', content_type: 'text/html' };
27-
return responseFactory(body, {
28-
status: 200,
29-
});
30-
});
23+
const result = await client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' }));
24+
assertEquals(result.content_type, 'text/html');
25+
assertEquals(result.data, 'a document summary');
26+
assertEquals(fetchStub.calls.length, 1);
3127

32-
const result = await client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' }));
33-
expect(result).toBeDefined();
34-
expect(result.content_type).toBe('text/html');
35-
expect(result.data).toBe('a document summary');
36-
expect(spy).toHaveBeenCalledTimes(1);
37-
});
28+
fetchStub.restore();
29+
});
3830

39-
it('fails due to failing to invalid config', async () => {
40-
const html = 'very long html file';
41-
await expect(
42-
client.extract(
31+
Deno.test('extract: fails due to invalid config', async () => {
32+
const KEY = '__API_KEY__';
33+
const client = new ScrapflyClient({ key: KEY });
34+
const html = 'very long html file';
35+
await assertRejects(
36+
async () => {
37+
await client.extract(
4338
new ExtractionConfig({
4439
body: html,
4540
content_type: 'text/html',
4641
ephemeral_template: { source: 'html' },
4742
template: 'template',
4843
}),
49-
),
50-
).rejects.toThrow(errors.ExtractionConfigError);
51-
});
44+
);
45+
},
46+
errors.ExtractionConfigError
47+
);
48+
});
5249

53-
it('fails to invalid API key', async () => {
54-
const html = 'very long html file';
55-
jest.spyOn(client, 'fetch').mockImplementation(async (): Promise<any> => {
56-
const result = {
57-
status: 'error',
58-
http_code: 401,
59-
reason: 'Unauthorized',
60-
error_id: '301e2d9e-b4f5-4289-85ea-e452143338df',
61-
message: 'Invalid API key',
62-
};
63-
return responseFactory(result, {
64-
status: 401,
65-
headers: {
66-
'Content-Type': 'application/json',
67-
},
68-
});
50+
Deno.test('extract: fails due to invalid API key', async () => {
51+
const KEY = '__API_KEY__';
52+
const client = new ScrapflyClient({ key: KEY });
53+
const html = 'very long html file';
54+
const fetchStub = stub(client, 'fetch', async (config: Request): Promise<Response> => {
55+
const result = {
56+
status: 'error',
57+
http_code: 401,
58+
reason: 'Unauthorized',
59+
error_id: '301e2d9e-b4f5-4289-85ea-e452143338df',
60+
message: 'Invalid API key',
61+
};
62+
return responseFactory(result, {
63+
status: 401,
64+
headers: {
65+
'Content-Type': 'application/json',
66+
},
6967
});
70-
await expect(client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' }))).rejects.toThrow(
71-
errors.BadApiKeyError,
72-
);
7368
});
7469

75-
it('fails to any extraction related error', async () => {
76-
const html = 'very long html file';
77-
jest.spyOn(client, 'fetch').mockImplementation(async (): Promise<any> => {
78-
const result = {
79-
code: 'ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED',
80-
error_id: 'f0e9a6af-846a-49ab-8321-e21bb12bf494',
81-
http_code: 422,
82-
links: {
83-
'Related Error Doc':
84-
'https://scrapfly.io/docs/extraction-api/error/ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED',
85-
},
86-
message: 'ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED',
87-
};
88-
return responseFactory(result, {
89-
status: 422,
90-
headers: {
91-
'Content-Type': 'application/json',
92-
},
93-
});
70+
await assertRejects(
71+
async () => {
72+
await client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' }));
73+
},
74+
errors.BadApiKeyError
75+
);
76+
77+
fetchStub.restore();
78+
});
79+
80+
Deno.test('extract: fails due to any extraction related error', async () => {
81+
const KEY = '__API_KEY__';
82+
const client = new ScrapflyClient({ key: KEY });
83+
const html = 'very long html file';
84+
const fetchStub = stub(client, 'fetch', async (config: Request): Promise<Response> => {
85+
const result = {
86+
code: 'ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED',
87+
error_id: 'f0e9a6af-846a-49ab-8321-e21bb12bf494',
88+
http_code: 422,
89+
links: {
90+
'Related Error Doc':
91+
'https://scrapfly.io/docs/extraction-api/error/ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED',
92+
},
93+
message: 'ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED',
94+
};
95+
return responseFactory(result, {
96+
status: 422,
97+
headers: {
98+
'Content-Type': 'application/json',
99+
},
94100
});
95-
await expect(client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' }))).rejects.toThrow(
96-
errors.ExtractionApiError,
97-
);
98101
});
99-
});
102+
103+
await assertRejects(
104+
async () => {
105+
await client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' }));
106+
},
107+
errors.ExtractionApiError
108+
);
109+
110+
fetchStub.restore();
111+
});

__tests__/client/init.test.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import * as errors from '../../src/errors.js';
2-
import { ScrapflyClient } from '../../src/client.js';
1+
import * as errors from '../../src/errors.ts';
2+
import { ScrapflyClient } from '../../src/client.ts';
3+
import { assertEquals, assertThrows } from "https://deno.land/std@0.224.0/assert/mod.ts";
34

4-
import { describe, it, expect } from '@jest/globals';
5-
6-
describe('client init', () => {
7-
it('success', async () => {
8-
const client = new ScrapflyClient({ key: '1234' });
9-
expect(client).toBeDefined();
10-
});
11-
12-
it('invalid key', async () => {
13-
expect(() => {
14-
new ScrapflyClient({ key: null });
15-
}).toThrow(errors.BadApiKeyError);
16-
});
5+
Deno.test('client init: success', () => {
6+
const client = new ScrapflyClient({ key: '1234' });
7+
assertEquals(!!client, true, "client should be defined");
178
});
9+
10+
Deno.test('client init: invalid key', () => {
11+
assertThrows(
12+
() => {
13+
new ScrapflyClient({ key: "" });
14+
},
15+
errors.BadApiKeyError,
16+
"Invalid key. Key must be a non-empty string"
17+
);
18+
});

0 commit comments

Comments
 (0)