aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Mayer <[email protected]>2025-02-10 02:13:06 +0700
committerGitHub <[email protected]>2025-02-09 20:13:06 +0100
commit9e1395380cf9baf9f0350c43cbbc303430e77dfb (patch)
treea2f7e1a0cf50022e63192c8f1d58c4ace6b24884
parentf9dd56009688f73ab6e7090083f5678741439dd5 (diff)
downloadfaker-9e1395380cf9baf9f0350c43cbbc303430e77dfb.tar.xz
faker-9e1395380cf9baf9f0350c43cbbc303430e77dfb.zip
feat(image): add AI-generated avatars (#3126)
-rw-r--r--src/modules/image/index.ts50
-rw-r--r--test/integration/modules/image.spec.ts7
-rw-r--r--test/modules/__snapshots__/image.spec.ts.snap30
-rw-r--r--test/modules/image.spec.ts29
-rw-r--r--test/scripts/apidocs/__snapshots__/verify-jsdoc-tags.spec.ts.snap1
-rw-r--r--test/scripts/apidocs/verify-jsdoc-tags.spec.ts2
6 files changed, 112 insertions, 7 deletions
diff --git a/src/modules/image/index.ts b/src/modules/image/index.ts
index 2275cbeb..6b0a7017 100644
--- a/src/modules/image/index.ts
+++ b/src/modules/image/index.ts
@@ -1,6 +1,7 @@
import { toBase64 } from '../../internal/base64';
import { deprecated } from '../../internal/deprecated';
import { ModuleBase } from '../../internal/module-base';
+import type { SexType } from '../person';
/**
* Module to generate images.
@@ -11,7 +12,7 @@ import { ModuleBase } from '../../internal/module-base';
*
* For a random placeholder image containing only solid color and text, use [`urlPlaceholder()`](https://fakerjs.dev/api/image.html#urlplaceholder) (uses a third-party service) or [`dataUri()`](https://fakerjs.dev/api/image.html#datauri) (returns a SVG string).
*
- * For a random user avatar image, use [`avatar()`](https://fakerjs.dev/api/image.html#avatar).
+ * For a random user avatar image, use [`avatar()`](https://fakerjs.dev/api/image.html#avatar), or [`personPortrait()`](https://fakerjs.dev/api/image.html#personportrait) which has more control over the size and sex of the person.
*
* If you need more control over the content of the images, you can pass a `category` parameter e.g. `'cat'` or `'nature'` to [`urlLoremFlickr()`](https://fakerjs.dev/api/image.html#urlloremflickr) or simply use [`faker.helpers.arrayElement()`](https://fakerjs.dev/api/helpers.html#arrayelement) with your own array of image URLs.
*/
@@ -27,7 +28,11 @@ export class ImageModule extends ModuleBase {
*/
avatar(): string {
// Add new avatar providers here, when adding a new one.
- return this.avatarGitHub();
+ const avatarMethod = this.faker.helpers.arrayElement([
+ this.personPortrait,
+ this.avatarGitHub,
+ ]);
+ return avatarMethod();
}
/**
@@ -46,6 +51,45 @@ export class ImageModule extends ModuleBase {
}
/**
+ * Generates a random square portrait (avatar) of a person.
+ * These are static images of fictional people created by an AI, Stable Diffusion 3.
+ * The image URLs are served via the JSDelivr CDN and subject to their [terms of use](https://www.jsdelivr.com/terms).
+ *
+ * @param options Options for generating an AI avatar.
+ * @param options.sex The sex of the person for the avatar. Can be `'female'` or `'male'`. If not provided, defaults to a random selection.
+ * @param options.size The size of the image. Can be `512`, `256`, `128`, `64` or `32`. If not provided, defaults to `512`.
+ *
+ * @example
+ * faker.image.personPortrait() // 'https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/57.jpg'
+ * faker.image.personPortrait({ sex: 'male', size: '128' }) // 'https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/male/128/27.jpg'
+ *
+ * @since 9.5.0
+ */
+ personPortrait(
+ options: {
+ /**
+ * The sex of the person for the avatar.
+ * Can be `'female'` or `'male'`.
+ *
+ * @default faker.person.sexType()
+ */
+ sex?: SexType;
+ /**
+ * The size of the image.
+ * Can be `512`, `256`, `128`, `64` or `32`.
+ *
+ * @default 512
+ */
+ size?: 512 | 256 | 128 | 64 | 32;
+ } = {}
+ ): string {
+ const { sex = this.faker.person.sexType(), size = 512 } = options;
+ const baseURL =
+ 'https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait';
+ return `${baseURL}/${sex}/${size}/${this.faker.number.int({ min: 0, max: 99 })}.jpg`;
+ }
+
+ /**
* Generates a random avatar from `https://cloudflare-ipfs.com/ipfs/Qmd3W5DuhgHirLHGVixi6V76LhCkZUz6pnFt5AJBiyvHye/avatar`.
*
* @example
@@ -59,7 +103,7 @@ export class ImageModule extends ModuleBase {
avatarLegacy(): string {
deprecated({
deprecated: 'faker.image.avatarLegacy()',
- proposed: 'faker.image.avatar()',
+ proposed: 'faker.image.avatar() or faker.image.personPortrait()',
since: '9.0.2',
until: '10.0.0',
});
diff --git a/test/integration/modules/image.spec.ts b/test/integration/modules/image.spec.ts
index 611317c6..5ec9e34b 100644
--- a/test/integration/modules/image.spec.ts
+++ b/test/integration/modules/image.spec.ts
@@ -74,6 +74,13 @@ describe('image', () => {
});
});
+ describe('personPortrait', () => {
+ it('should return a random asset url', async () => {
+ const actual = faker.image.personPortrait();
+ await assertWorkingUrl(actual);
+ });
+ });
+
describe('url', () => {
it('should return a random image url', async () => {
const actual = faker.image.url();
diff --git a/test/modules/__snapshots__/image.spec.ts.snap b/test/modules/__snapshots__/image.spec.ts.snap
index bccdada4..2bcbe1a3 100644
--- a/test/modules/__snapshots__/image.spec.ts.snap
+++ b/test/modules/__snapshots__/image.spec.ts.snap
@@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-exports[`image > 42 > avatar 1`] = `"https://avatars.githubusercontent.com/u/37454012"`;
+exports[`image > 42 > avatar 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/male/512/73.jpg"`;
exports[`image > 42 > avatarGitHub 1`] = `"https://avatars.githubusercontent.com/u/37454012"`;
@@ -22,6 +22,14 @@ exports[`image > 42 > dataUri > with width 1`] = `"data:image/svg+xml;base64,PHN
exports[`image > 42 > dataUri > with width and height 1`] = `"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22128%22%20height%3D%22128%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%238ead33%22%2F%3E%3Ctext%20x%3D%2264%22%20y%3D%2264%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E128x128%3C%2Ftext%3E%3C%2Fsvg%3E"`;
+exports[`image > 42 > personPortrait > noArgs 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/95.jpg"`;
+
+exports[`image > 42 > personPortrait > with sex 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/37.jpg"`;
+
+exports[`image > 42 > personPortrait > with sex and size 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/male/256/37.jpg"`;
+
+exports[`image > 42 > personPortrait > with size 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/128/95.jpg"`;
+
exports[`image > 42 > url > noArgs 1`] = `"https://picsum.photos/seed/993RBH1Y/1498/3802"`;
exports[`image > 42 > url > with height 1`] = `"https://picsum.photos/seed/B993RBH1Y/1498/128"`;
@@ -76,7 +84,7 @@ exports[`image > 42 > urlPlaceholder > with width 1`] = `"https://via.placeholde
exports[`image > 42 > urlPlaceholder > with width and height 1`] = `"https://via.placeholder.com/128x128/8ead33/1ddf0f.webp?text=benevolentia%20attonbitus%20auctus"`;
-exports[`image > 1211 > avatar 1`] = `"https://avatars.githubusercontent.com/u/92852016"`;
+exports[`image > 1211 > avatar 1`] = `"https://avatars.githubusercontent.com/u/89347165"`;
exports[`image > 1211 > avatarGitHub 1`] = `"https://avatars.githubusercontent.com/u/92852016"`;
@@ -98,6 +106,14 @@ exports[`image > 1211 > dataUri > with width 1`] = `"data:image/svg+xml;charset=
exports[`image > 1211 > dataUri > with width and height 1`] = `"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgYmFzZVByb2ZpbGU9ImZ1bGwiIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWQ0ZmVmIi8+PHRleHQgeD0iNjQiIHk9IjY0IiBmb250LXNpemU9IjIwIiBhbGlnbm1lbnQtYmFzZWxpbmU9Im1pZGRsZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0id2hpdGUiPjEyOHgxMjg8L3RleHQ+PC9zdmc+"`;
+exports[`image > 1211 > personPortrait > noArgs 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/male/512/89.jpg"`;
+
+exports[`image > 1211 > personPortrait > with sex 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/92.jpg"`;
+
+exports[`image > 1211 > personPortrait > with sex and size 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/male/256/92.jpg"`;
+
+exports[`image > 1211 > personPortrait > with size 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/male/128/89.jpg"`;
+
exports[`image > 1211 > url > noArgs 1`] = `"https://loremflickr.com/3714/3573?lock=8982492793493979"`;
exports[`image > 1211 > url > with height 1`] = `"https://picsum.photos/seed/ZFGLlH/3714/128"`;
@@ -152,7 +168,7 @@ exports[`image > 1211 > urlPlaceholder > with width 1`] = `"https://via.placehol
exports[`image > 1211 > urlPlaceholder > with width and height 1`] = `"https://via.placeholder.com/128x128/ed4fef/a7fbae.webp?text=dapifer%20usque%20unde"`;
-exports[`image > 1337 > avatar 1`] = `"https://avatars.githubusercontent.com/u/26202467"`;
+exports[`image > 1337 > avatar 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/27.jpg"`;
exports[`image > 1337 > avatarGitHub 1`] = `"https://avatars.githubusercontent.com/u/26202467"`;
@@ -174,6 +190,14 @@ exports[`image > 1337 > dataUri > with width 1`] = `"data:image/svg+xml;base64,P
exports[`image > 1337 > dataUri > with width and height 1`] = `"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22128%22%20height%3D%22128%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23536a7b%22%2F%3E%3Ctext%20x%3D%2264%22%20y%3D%2264%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E128x128%3C%2Ftext%3E%3C%2Fsvg%3E"`;
+exports[`image > 1337 > personPortrait > noArgs 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/15.jpg"`;
+
+exports[`image > 1337 > personPortrait > with sex 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/26.jpg"`;
+
+exports[`image > 1337 > personPortrait > with sex and size 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/male/256/26.jpg"`;
+
+exports[`image > 1337 > personPortrait > with size 1`] = `"https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/128/15.jpg"`;
+
exports[`image > 1337 > url > noArgs 1`] = `"https://loremflickr.com/1048/635?lock=4137158724208997"`;
exports[`image > 1337 > url > with height 1`] = `"https://loremflickr.com/1048/128?lock=2505140979113303"`;
diff --git a/test/modules/image.spec.ts b/test/modules/image.spec.ts
index 2f9b0f02..0d494cb4 100644
--- a/test/modules/image.spec.ts
+++ b/test/modules/image.spec.ts
@@ -108,6 +108,13 @@ describe('image', () => {
type: 'svg-uri',
});
});
+
+ t.describe('personPortrait', (t) => {
+ t.it('noArgs')
+ .it('with sex', { sex: 'female' })
+ .it('with size', { size: 128 })
+ .it('with sex and size', { sex: 'male', size: 256 });
+ });
});
describe('avatar', () => {
@@ -144,6 +151,28 @@ describe('image', () => {
});
});
+ describe('personPortrait', () => {
+ it('should return a random avatar url from AI', () => {
+ const imageUrl = faker.image.personPortrait();
+
+ expect(imageUrl).toBeTypeOf('string');
+ expect(imageUrl).toMatch(
+ /^https:\/\/cdn\.jsdelivr\.net\/gh\/faker-js\/assets-person-portrait\/(female|male)\/512\/\d{1,2}\.jpg$/
+ );
+ expect(() => new URL(imageUrl)).not.toThrow();
+ });
+
+ it('should return a random avatar url from AI with fixed size and sex', () => {
+ const imageUrl = faker.image.personPortrait({ sex: 'male', size: 128 });
+
+ expect(imageUrl).toBeTypeOf('string');
+ expect(imageUrl).toMatch(
+ /^https:\/\/cdn\.jsdelivr\.net\/gh\/faker-js\/assets-person-portrait\/male\/128\/\d{1,2}\.jpg$/
+ );
+ expect(() => new URL(imageUrl)).not.toThrow();
+ });
+ });
+
describe('url', () => {
it('should return a random image url', () => {
const actual = faker.image.url();
diff --git a/test/scripts/apidocs/__snapshots__/verify-jsdoc-tags.spec.ts.snap b/test/scripts/apidocs/__snapshots__/verify-jsdoc-tags.spec.ts.snap
index c13b2811..07983094 100644
--- a/test/scripts/apidocs/__snapshots__/verify-jsdoc-tags.spec.ts.snap
+++ b/test/scripts/apidocs/__snapshots__/verify-jsdoc-tags.spec.ts.snap
@@ -243,6 +243,7 @@ exports[`check docs completeness > all modules and methods are present 1`] = `
"avatarGitHub",
"avatarLegacy",
"dataUri",
+ "personPortrait",
"url",
"urlLoremFlickr",
"urlPicsumPhotos",
diff --git a/test/scripts/apidocs/verify-jsdoc-tags.spec.ts b/test/scripts/apidocs/verify-jsdoc-tags.spec.ts
index 703e42cf..dd77620d 100644
--- a/test/scripts/apidocs/verify-jsdoc-tags.spec.ts
+++ b/test/scripts/apidocs/verify-jsdoc-tags.spec.ts
@@ -147,7 +147,7 @@ ${examples}`;
if (!examples.includes('import ')) {
const imports = [
// collect the imports for the various locales e.g. fakerDE_CH
- ...new Set(examples.match(/(?<!\.)faker[^.]*(?=\.)/g)),
+ ...new Set(examples.match(/(?<!\.)faker[^.-]*(?=\.)/g)),
];
if (imports.length > 0) {