Files
2026-07-13 13:23:39 +08:00

223 lines
8.1 KiB
TypeScript

import fs from 'node:fs';
import path from 'node:path';
import { downloadListOfUrls, extractUrls, URL_WITH_COMMAS_REGEX } from '@crawlee/utils';
vitest.mock('@crawlee/utils/src/internals/gotScraping', async () => {
return {
gotScraping: vitest.fn(),
};
});
const baseDataPath = path.join(__dirname, '..', 'shared', 'data');
describe('downloadListOfUrls()', () => {
test('downloads a list of URLs', async () => {
const text = fs.readFileSync(path.join(baseDataPath, 'simple_url_list.txt'), 'utf8');
const arr = text
.trim()
.split(/[\r\n]+/g)
.map((u) => u.trim());
// @ts-ignore for some reason, this fails when the project is not built :/
const { gotScraping } = await import('@crawlee/utils');
const gotScrapingSpy = vitest.mocked(gotScraping);
gotScrapingSpy.mockResolvedValueOnce({ body: text });
await expect(
downloadListOfUrls({
url: 'http://www.nowhere12345.com',
}),
).resolves.toEqual(arr);
});
});
describe('extractUrls()', () => {
const SIMPLE_URL_LIST = 'simple_url_list.txt';
const UNICODE_URL_LIST = 'unicode_url_list.txt';
const COMMA_URL_LIST = 'unicode+comma_url_list.txt';
const TRICKY_URL_LIST = 'tricky_url_list.txt';
const INVALID_URL_LIST = 'invalid_url_list.txt';
const getURLData = (filename: string) => {
const string = fs.readFileSync(path.join(baseDataPath, filename), 'utf8');
const array = string
.trim()
.split(/[\r\n]+/g)
.map((u) => u.trim());
return { string, array };
};
const makeJSON = ({ string, array }: { string: string; array: string[] }) =>
JSON.stringify({
one: [{ http: string }],
two: array.map((url) => ({ num: 123, url })),
});
const makeCSV = (array: string[], delimiter?: string) =>
array.map((url) => ['ABC', 233, url, '.'].join(delimiter || ',')).join('\n');
const makeText = (array: string[]) => {
const text = fs.readFileSync(path.join(baseDataPath, 'lipsum.txt'), 'utf8').split('');
const ID = 'ů';
const maxIndex = text.length - 1;
array.forEach((__, index) => {
const indexInText = (index * 17) % maxIndex;
if (text[indexInText] === ID) {
text[indexInText + 1] = ID;
} else {
text[indexInText] = ID;
}
});
return array.reduce((string, url) => string.replace(ID, ` ${url} `), text.join(''));
};
test('extracts simple URLs', () => {
const { string, array } = getURLData(SIMPLE_URL_LIST);
const extracted = extractUrls({ string });
expect(extracted).toEqual(array);
});
test('extracts unicode URLs', () => {
const { string, array } = getURLData(UNICODE_URL_LIST);
const extracted = extractUrls({ string });
expect(extracted).toEqual(array);
});
test('extracts unicode URLs with commas', () => {
const { string, array } = getURLData(COMMA_URL_LIST);
const extracted = extractUrls({ string, urlRegExp: URL_WITH_COMMAS_REGEX });
expect(extracted).toEqual(array);
});
test('extracts tricky URLs', () => {
const { string, array } = getURLData(TRICKY_URL_LIST);
const extracted = extractUrls({ string });
expect(extracted).toEqual(array);
});
// https://github.com/apify/crawlee/issues/2755
test('extracts URLs with apostrophes in the path', () => {
const url = "https://www.zillow.com/homedetails/141-O'Canoe-Pl-Hertford-NC-27944/74398007_zpid/";
const extracted = extractUrls({ string: url });
expect(extracted).toEqual([url]);
});
test('extracts URLs with asterisks in the path', () => {
const url = 'https://example.com/path*star/end';
const extracted = extractUrls({ string: url });
expect(extracted).toEqual([url]);
});
test('does not extract invalid URLs', () => {
const { string } = getURLData(INVALID_URL_LIST);
const extracted = extractUrls({ string });
expect(extracted).toEqual(['http://www.foo.bar']);
});
test('extracts simple URLs from JSON', () => {
const d = getURLData(SIMPLE_URL_LIST);
const string = makeJSON(d);
const extracted = extractUrls({ string });
expect(extracted).toEqual(d.array.concat(d.array));
});
test('extracts unicode URLs from JSON', () => {
const d = getURLData(UNICODE_URL_LIST);
const string = makeJSON(d);
const extracted = extractUrls({ string });
expect(extracted).toEqual(d.array.concat(d.array));
});
test('extracts unicode URLs with commas from JSON', () => {
const d = getURLData(COMMA_URL_LIST);
const string = makeJSON(d);
const extracted = extractUrls({ string, urlRegExp: URL_WITH_COMMAS_REGEX });
expect(extracted).toEqual(d.array.concat(d.array));
});
test('extracts tricky URLs from JSON', () => {
const d = getURLData(TRICKY_URL_LIST);
const string = makeJSON(d);
const extracted = extractUrls({ string });
expect(extracted).toEqual(d.array.concat(d.array));
});
test('does not extract invalid URLs from JSON', () => {
const d = getURLData(INVALID_URL_LIST);
const string = makeJSON(d);
const extracted = extractUrls({ string });
expect(extracted).toEqual(['http://www.foo.bar', 'http://www.foo.bar']);
});
test('extracts simple URLs from CSV', () => {
const { array } = getURLData(SIMPLE_URL_LIST);
const string = makeCSV(array);
const extracted = extractUrls({ string });
expect(extracted).toEqual(array);
});
test('extracts unicode URLs from CSV', () => {
const { array } = getURLData(UNICODE_URL_LIST);
const string = makeCSV(array);
const extracted = extractUrls({ string });
expect(extracted).toEqual(array);
});
test('extracts unicode URLs with commas from semicolon CSV', () => {
const { array } = getURLData(COMMA_URL_LIST);
const string = makeCSV(array, ';');
const extracted = extractUrls({ string, urlRegExp: URL_WITH_COMMAS_REGEX });
expect(extracted).toEqual(array);
});
test('extracts tricky URLs from CSV', () => {
const { array } = getURLData(TRICKY_URL_LIST);
const string = makeCSV(array);
const extracted = extractUrls({ string });
expect(extracted).toEqual(array);
});
test('does not extract invalid URLs from CSV', () => {
const { array } = getURLData(INVALID_URL_LIST);
const string = makeCSV(array);
const extracted = extractUrls({ string });
expect(extracted).toEqual(['http://www.foo.bar']);
});
test('extracts simple URLs from Text', () => {
const { array } = getURLData(SIMPLE_URL_LIST);
const string = makeText(array);
const extracted = extractUrls({ string });
expect(extracted).toEqual(array);
});
test('extracts unicode URLs from Text', () => {
const { array } = getURLData(UNICODE_URL_LIST);
const string = makeText(array);
const extracted = extractUrls({ string });
expect(extracted).toEqual(array);
});
test('extracts unicode URLs with commas from Text', () => {
const { array } = getURLData(COMMA_URL_LIST);
const string = makeText(array);
const extracted = extractUrls({ string, urlRegExp: URL_WITH_COMMAS_REGEX });
expect(extracted).toEqual(array);
});
test('extracts tricky URLs from Text', () => {
const { array } = getURLData(TRICKY_URL_LIST);
const string = makeText(array);
const extracted = extractUrls({ string });
expect(extracted).toEqual(array);
});
test('does not extract invalid URLs from Text', () => {
const { array } = getURLData(INVALID_URL_LIST);
const string = makeText(array);
const extracted = extractUrls({ string });
expect(extracted).toEqual(['http://www.foo.bar']);
});
});