Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions packages/generate/src/field-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ function normalize(fieldName: string): string {
/**
* String field rules, in precedence order. Date-ness runs first so fields
* like `emailVerifiedAt` produce a date rather than an email; username and
* filename run before the generic person-name rule so `userName`/`fileName`
* filename run before the person-name rules so `userName`/`fileName`
* (which contain the word "name") aren't mistaken for a person's name.
*
* Person names are split per variant: `firstName` gets a given name,
* `lastName` a family name, and only `name`/`fullName`/`displayName` get a
* full name — a pasted `User` model is the most common input there is, and
* `{ firstName: "Claudine Kuhn" }` reads as wrong at a glance.
*/
export const STRING_RULES: FieldRule[] = [
{
Expand All @@ -72,9 +77,18 @@ export const STRING_RULES: FieldRule[] = [
use: (faker) => `${faker.lorem.slug()}.txt`,
},
{
name: 'person',
when: (n) =>
['name', 'firstname', 'lastname', 'fullname', 'displayname'].includes(normalize(n)),
name: 'first-name',
when: (n) => ['firstname', 'givenname', 'forename'].includes(normalize(n)),
use: (faker) => faker.person.firstName(),
},
{
name: 'last-name',
when: (n) => ['lastname', 'surname', 'familyname'].includes(normalize(n)),
use: (faker) => faker.person.lastName(),
},
{
name: 'full-name',
when: (n) => ['name', 'fullname', 'displayname'].includes(normalize(n)),
use: (faker) => faker.person.fullName(),
},
{
Expand Down
39 changes: 36 additions & 3 deletions packages/generate/src/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
expect(value.identifier).toBeGreaterThanOrEqual(0)
// id and _id should be sequential (1, 2, ...)
expect(value.id).toBe(1)
expect(value._id).toBe(2)

Check warning on line 112 in packages/generate/src/generate.test.ts

View workflow job for this annotation

GitHub Actions / Validate

eslint(no-underscore-dangle)

packages/generate/src/generate.test.ts:112:18: Unexpected dangling '_' in '`_id`'.
// Foreign keys should be in range [1, 1000]
expect(value.userId).toBeGreaterThanOrEqual(1)
expect(value.userId).toBeLessThanOrEqual(1000)
Expand Down Expand Up @@ -144,6 +144,27 @@
expect(typeof value.orderId).toBe('number')
})

it('gives firstName, lastName and fullName each the matching kind of name', async () => {
// Plan 6 audit, finding #6: a pasted `User` used to come out as
// `{ firstName: "Claudine Kuhn", lastName: "Edwin Bode" }`.
const shape: Shape = {
kind: 'object',
fields: [
{ name: 'firstName', shape: primitive('string'), optional: false },
{ name: 'lastName', shape: primitive('string'), optional: false },
{ name: 'fullName', shape: primitive('string'), optional: false },
{ name: 'name', shape: primitive('string'), optional: false },
],
}
for (const seed of [1, 7, 42, 1234]) {
const value = (await generate(shape, { seed })) as Record<string, string>
expect(value.firstName).not.toMatch(/\s/)
expect(value.lastName).not.toMatch(/\s/)
expect(value.fullName).toMatch(/\S\s+\S/)
expect(value.name).toMatch(/\S\s+\S/)
}
})

it('arrayLength is clamped to 1..1000', async () => {
const shape = { kind: 'array' as const, items: primitive('integer') }
expect((await generate(shape, { seed: 1, arrayLength: 0 })) as unknown[]).toHaveLength(1)
Expand Down Expand Up @@ -305,10 +326,22 @@
rejects: ['name', 'title'],
},
{
rule: 'person',
rule: 'first-name',
type: 'string',
accepts: ['firstName', 'first_name', 'givenName'],
rejects: ['name', 'lastName', 'filename', 'username'],
},
{
rule: 'last-name',
type: 'string',
accepts: ['lastName', 'last_name', 'surname', 'familyName'],
rejects: ['name', 'firstName', 'filename', 'username'],
},
{
rule: 'full-name',
type: 'string',
accepts: ['name', 'firstName', 'displayName', 'full_name'],
rejects: ['filename', 'username', 'nameless', 'renamed'],
accepts: ['name', 'displayName', 'full_name'],
rejects: ['firstName', 'lastName', 'filename', 'username', 'nameless', 'renamed'],
},
{
rule: 'phone',
Expand Down
Loading