aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorST-DDT <[email protected]>2024-11-09 11:32:15 +0100
committerGitHub <[email protected]>2024-11-09 10:32:15 +0000
commit9ecf99b75763ed08e19f3115c01a5e58aeaf529c (patch)
treea39162eea4c1b81568f192fbf5e8c8049c907479
parent0d850758d0ea0db45a9a4c8abda5c1e09796fb44 (diff)
downloadfaker-9ecf99b75763ed08e19f3115c01a5e58aeaf529c.tar.xz
faker-9ecf99b75763ed08e19f3115c01a5e58aeaf529c.zip
test: verify the generated image links are working (#3127)
-rw-r--r--.github/workflows/integration-test.yml41
-rw-r--r--package.json1
-rw-r--r--test/integration/modules/image.spec.ts119
-rw-r--r--test/modules/image.spec.ts129
-rw-r--r--vitest.config.ts2
-rw-r--r--vitest.it-config.ts14
6 files changed, 250 insertions, 56 deletions
diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml
new file mode 100644
index 00000000..48f4b6d7
--- /dev/null
+++ b/.github/workflows/integration-test.yml
@@ -0,0 +1,41 @@
+name: Integration Test
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '0 3 * * 1' # weekly on Mondays at 03:00
+
+permissions:
+ contents: read # to fetch code (actions/checkout)
+
+jobs:
+ integration_test:
+ name: Integration Test
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ with:
+ fetch-depth: 0
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
+
+ - name: Set node version to 22
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
+ with:
+ node-version: 22
+ cache: 'pnpm'
+
+ - name: Install deps
+ run: pnpm install
+ env:
+ CYPRESS_INSTALL_BINARY: 0
+
+ - name: Build
+ run: pnpm run build
+
+ - name: Integration Test
+ run: pnpm run integration-test
diff --git a/package.json b/package.json
index e193e629..a0ccb6ac 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"test": "vitest",
"test:update-snapshots": "vitest run -u",
"coverage": "vitest run --coverage",
+ "integration-test": "vitest -c vitest.it-config.ts",
"cypress": "cypress",
"docs:test:e2e:ci": "run-s docs:build:ci docs:test:e2e:run",
"docs:test:e2e:run": "run-p --race docs:serve \"cypress run\"",
diff --git a/test/integration/modules/image.spec.ts b/test/integration/modules/image.spec.ts
new file mode 100644
index 00000000..0901b99b
--- /dev/null
+++ b/test/integration/modules/image.spec.ts
@@ -0,0 +1,119 @@
+/*
+ * Integration tests for the image methods ensuring that the returned urls work.
+ */
+import https from 'node:https';
+import { resolve as urlResolve } from 'node:url';
+import { describe, expect, it } from 'vitest';
+import { faker } from '../../../src';
+
+/**
+ * Checks that the given address is a working https address.
+ *
+ * An address is considered working, if it:
+ *
+ * - is a string
+ * - starts with https
+ * - is a proper url
+ * - returns a http-200 (after redirects)
+ *
+ * There is a separate unit test file for checking if the returned URL matches the expectations (domain, parameters, etc.).
+ *
+ * @param address The address to check.
+ */
+async function assertWorkingUrl(address: string): Promise<void> {
+ expect(address).toBeTypeOf('string');
+ expect(address).toMatch(/^https:\/\//);
+ expect(() => new URL(address)).not.toThrow();
+
+ await expect(
+ new Promise((resolve, reject) => {
+ https
+ .get(address, ({ statusCode, headers: { location } }) => {
+ if (statusCode == null) {
+ reject(new Error(`No StatusCode, expected 200`));
+ } else if (statusCode === 200) {
+ resolve(true);
+ } else if (statusCode >= 300 && statusCode < 400 && location) {
+ const newAddress = urlResolve(address, location);
+ assertWorkingUrl(newAddress)
+ .then(() => resolve(true))
+ .catch((error: unknown) => {
+ reject(
+ new Error(`Failed to resolve redirect to '${location}'`, {
+ cause: error,
+ })
+ );
+ });
+ } else {
+ reject(
+ new Error(
+ `Bad StatusCode ${statusCode} expected 200 for '${location}'`
+ )
+ );
+ }
+ })
+ .on('error', (error: unknown) => {
+ reject(new Error(`Failed to get '${address}'`, { cause: error }));
+ });
+ })
+ ).resolves.toBe(true);
+}
+
+describe('image', () => {
+ describe('avatar', () => {
+ it('should return a random avatar url', async () => {
+ const actual = faker.image.avatar();
+ await assertWorkingUrl(actual);
+ });
+ });
+
+ describe('avatarGitHub', () => {
+ it('should return a random avatar url from GitHub', async () => {
+ const actual = faker.image.avatarGitHub();
+ await assertWorkingUrl(actual);
+ });
+ });
+
+ describe('url', () => {
+ it('should return a random image url', async () => {
+ const actual = faker.image.url();
+ await assertWorkingUrl(actual);
+ });
+
+ it('should return a random image url with a width', async () => {
+ const actual = faker.image.url({ width: 100 });
+ await assertWorkingUrl(actual);
+ });
+
+ it('should return a random image url with a height', async () => {
+ const actual = faker.image.url({ height: 100 });
+ await assertWorkingUrl(actual);
+ });
+
+ it('should return a random image url with a width and height', async () => {
+ const actual = faker.image.url({ width: 128, height: 64 });
+ await assertWorkingUrl(actual);
+ });
+ });
+
+ describe('urlLoremFlickr', () => {
+ it('should return a random image url from LoremFlickr', async () => {
+ const actual = faker.image.urlLoremFlickr();
+ await assertWorkingUrl(actual);
+ });
+ });
+
+ describe('urlPicsumPhotos', () => {
+ it('should return a random image url from PicsumPhotos', async () => {
+ const actual = faker.image.urlPicsumPhotos();
+ await assertWorkingUrl(actual);
+ });
+ });
+
+ describe('urlPlaceholder', () => {
+ it('should return a random image url from Placeholder', async () => {
+ const actual = faker.image.urlPlaceholder();
+ await assertWorkingUrl(actual);
+ });
+ });
+});
diff --git a/test/modules/image.spec.ts b/test/modules/image.spec.ts
index 8624a965..36be582d 100644
--- a/test/modules/image.spec.ts
+++ b/test/modules/image.spec.ts
@@ -3,6 +3,25 @@ import { describe, expect, it } from 'vitest';
import { faker } from '../../src';
import { seededTests } from '../support/seeded-runs';
+/**
+ * Checks that the given address is a valid https address.
+ *
+ * An address is considered valid, if it:
+ *
+ * - is a string
+ * - starts with https
+ * - is a proper url
+ *
+ * There is a separate integretation test file for checking if the address is reachable.
+ *
+ * @param address The address to check.
+ */
+function assertValidUrl(address: string): void {
+ expect(address).toBeTypeOf('string');
+ expect(address).toMatch(/^https:\/\//);
+ expect(() => new URL(address)).not.toThrow();
+}
+
describe('image', () => {
seededTests(faker, 'image', (t) => {
t.itEach('avatar', 'avatarGitHub', 'avatarLegacy');
@@ -93,85 +112,78 @@ describe('image', () => {
describe('avatar', () => {
it('should return a random avatar url', () => {
- const avatarUrl = faker.image.avatar();
+ const actual = faker.image.avatar();
- expect(avatarUrl).toBeTypeOf('string');
- expect(avatarUrl).toMatch(/^https:\/\//);
- expect(() => new URL(avatarUrl)).not.toThrow();
+ assertValidUrl(actual);
});
});
describe('avatarGitHub', () => {
it('should return a random avatar url from GitHub', () => {
- const avatarUrl = faker.image.avatarGitHub();
+ const actual = faker.image.avatarGitHub();
- expect(avatarUrl).toBeTypeOf('string');
- expect(avatarUrl).toMatch(
+ expect(actual).toBeTypeOf('string');
+ expect(actual).toMatch(
/^https:\/\/avatars\.githubusercontent\.com\/u\/\d+$/
);
+ assertValidUrl(actual);
});
});
describe('avatarLegacy', () => {
it('should return a random avatar url from cloudflare-ipfs', () => {
// eslint-disable-next-line @typescript-eslint/no-deprecated
- const avatarUrl = faker.image.avatarLegacy();
+ const actual = faker.image.avatarLegacy();
- expect(avatarUrl).toBeTypeOf('string');
- expect(avatarUrl).toMatch(
+ expect(actual).toBeTypeOf('string');
+ expect(actual).toMatch(
/^https:\/\/cloudflare-ipfs\.com\/ipfs\/Qmd3W5DuhgHirLHGVixi6V76LhCkZUz6pnFt5AJBiyvHye\/avatar\/\d{1,4}\.jpg$/
);
+ // The links aren't working anymore - there is nothing we can do about it
+ // assertWebAddress(avatarUrl);
});
});
describe('url', () => {
it('should return a random image url', () => {
- const imageUrl = faker.image.url();
+ const actual = faker.image.url();
- expect(imageUrl).toBeTypeOf('string');
- expect(imageUrl).toMatch(/^https:\/\//);
- expect(() => new URL(imageUrl)).not.toThrow();
+ assertValidUrl(actual);
});
it('should return a random image url with a width', () => {
const width = 100;
- const imageUrl = faker.image.url({ width });
+ const actual = faker.image.url({ width });
- expect(imageUrl).toBeTypeOf('string');
- expect(imageUrl).toMatch(/^https:\/\//);
- expect(() => new URL(imageUrl)).not.toThrow();
- expect(imageUrl).include(`${width}`);
+ assertValidUrl(actual);
+ expect(actual).include(`${width}`);
});
it('should return a random image url with a height', () => {
const height = 100;
- const imageUrl = faker.image.url({ height });
+ const actual = faker.image.url({ height });
- expect(imageUrl).toBeTypeOf('string');
- expect(imageUrl).toMatch(/^https:\/\//);
- expect(() => new URL(imageUrl)).not.toThrow();
- expect(imageUrl).include(`${height}`);
+ assertValidUrl(actual);
+ expect(actual).include(`${height}`);
});
it('should return a random image url with a width and height', () => {
const width = 128;
const height = 64;
- const imageUrl = faker.image.url({ width, height });
+ const actual = faker.image.url({ width, height });
- expect(imageUrl).toBeTypeOf('string');
- expect(imageUrl).toMatch(/^https:\/\//);
- expect(() => new URL(imageUrl)).not.toThrow();
- expect(imageUrl).include(`${width}`);
- expect(imageUrl).include(`${height}`);
+ assertValidUrl(actual);
+ expect(actual).include(`${width}`);
+ expect(actual).include(`${height}`);
});
});
describe('urlLoremFlickr', () => {
it('should return a random image url from LoremFlickr', () => {
- const imageUrl = faker.image.urlLoremFlickr();
+ const actual = faker.image.urlLoremFlickr();
- expect(imageUrl).toBeTypeOf('string');
- expect(imageUrl).toMatch(
+ assertValidUrl(actual);
+ expect(actual).toMatch(
/^https:\/\/loremflickr\.com\/\d+\/\d+\?lock=\d+$/
);
});
@@ -179,10 +191,10 @@ describe('image', () => {
describe('urlPicsumPhotos', () => {
it('should return a random image url from PicsumPhotos', () => {
- const imageUrl = faker.image.urlPicsumPhotos();
+ const actual = faker.image.urlPicsumPhotos();
- expect(imageUrl).toBeTypeOf('string');
- expect(imageUrl).toMatch(
+ assertValidUrl(actual);
+ expect(actual).toMatch(
/^https:\/\/picsum\.photos\/seed\/[0-9a-zA-Z]+\/\d+\/\d+(\?(grayscale&?)?(blur=\d+)?)?$/
);
});
@@ -190,10 +202,10 @@ describe('image', () => {
describe('urlPlaceholder', () => {
it('should return a random image url from Placeholder', () => {
- const imageUrl = faker.image.urlPlaceholder();
+ const actual = faker.image.urlPlaceholder();
- expect(imageUrl).toBeTypeOf('string');
- expect(imageUrl).toMatch(
+ assertValidUrl(actual);
+ expect(actual).toMatch(
/^https:\/\/via\.placeholder\.com\/\d+x\d+\/[0-9a-fA-F]{6}\/[0-9a-fA-F]{6}\.[a-z]{3,4}\?text=.+$/
);
});
@@ -201,42 +213,47 @@ describe('image', () => {
describe('dataUri', () => {
it('should return an image data uri', () => {
- const dataUri = faker.image.dataUri();
- expect(dataUri).toMatch(/^data:image\/svg\+xml;/);
- expect(dataUri).toSatisfy(isDataURI);
+ const actual = faker.image.dataUri();
+
+ expect(actual).toMatch(/^data:image\/svg\+xml;/);
+ expect(actual).toSatisfy(isDataURI);
});
it('should return an uri-encoded image data uri', () => {
- const dataUri = faker.image.dataUri({ type: 'svg-uri' });
- expect(dataUri).toMatch(/^data:image\/svg\+xml;charset=UTF-8,/);
- expect(dataUri).toSatisfy(isDataURI);
+ const actual = faker.image.dataUri({ type: 'svg-uri' });
+
+ expect(actual).toMatch(/^data:image\/svg\+xml;charset=UTF-8,/);
+ expect(actual).toSatisfy(isDataURI);
});
it('should return a base64 image data uri', () => {
- const dataUri = faker.image.dataUri({ type: 'svg-base64' });
- expect(dataUri).toMatch(/^data:image\/svg\+xml;base64,/);
- expect(dataUri).toSatisfy(isDataURI);
+ const actual = faker.image.dataUri({ type: 'svg-base64' });
+
+ expect(actual).toMatch(/^data:image\/svg\+xml;base64,/);
+ expect(actual).toSatisfy(isDataURI);
});
it('should return an image data uri with fixed size', () => {
- const dataUri = faker.image.dataUri({
+ const actual = faker.image.dataUri({
width: 200,
height: 300,
type: 'svg-uri', // required for the regex check
});
- expect(dataUri).toMatch(/^data:image\/svg\+xml;charset=UTF-8,/);
- expect(dataUri).toMatch(/width%3D%22200%22%20height%3D%22300/);
- expect(dataUri).toSatisfy(isDataURI);
+
+ expect(actual).toMatch(/^data:image\/svg\+xml;charset=UTF-8,/);
+ expect(actual).toMatch(/width%3D%22200%22%20height%3D%22300/);
+ expect(actual).toSatisfy(isDataURI);
});
it('should return an image data uri with a fixed background color', () => {
- const dataUri = faker.image.dataUri({
+ const actual = faker.image.dataUri({
color: 'red',
type: 'svg-uri', // required for the regex check
});
- expect(dataUri).toMatch(/^data:image\/svg\+xml;charset=UTF-8,/);
- expect(dataUri).toMatch(/fill%3D%22red/);
- expect(dataUri).toSatisfy(isDataURI);
+
+ expect(actual).toMatch(/^data:image\/svg\+xml;charset=UTF-8,/);
+ expect(actual).toMatch(/fill%3D%22red/);
+ expect(actual).toSatisfy(isDataURI);
});
});
});
diff --git a/vitest.config.ts b/vitest.config.ts
index 94991a6f..24a9cda3 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -9,6 +9,8 @@ console.log('VITEST_SEQUENCE_SEED', VITEST_SEQUENCE_SEED);
export default defineConfig({
test: {
setupFiles: ['test/setup.ts'],
+ include: ['test/**/*.spec.ts'],
+ exclude: ['test/integration/**/*.spec.ts'],
coverage: {
all: true,
provider: 'v8',
diff --git a/vitest.it-config.ts b/vitest.it-config.ts
new file mode 100644
index 00000000..585b3d85
--- /dev/null
+++ b/vitest.it-config.ts
@@ -0,0 +1,14 @@
+import { defineConfig } from 'vitest/config';
+import config from './vitest.config';
+
+delete config.test?.coverage;
+delete config.test?.typecheck;
+delete config.test?.exclude;
+
+export default defineConfig({
+ test: {
+ ...config.test,
+ include: ['test/integration/**/*.spec.ts'],
+ testTimeout: 30000,
+ },
+});