Compare commits

...

9 Commits

Author SHA1 Message Date
Paul Popus
c2cd6bbaec fix test error 2024-07-29 17:04:22 -04:00
Paul Popus
b2657ed12b fix tests 2024-07-29 14:02:28 -04:00
Paul Popus
0851ef99da update tests 2024-07-29 13:31:22 -04:00
Paul Popus
3c483ab57a update tests 2024-07-29 12:35:11 -04:00
Paul Popus
9bdbbb674f capitalise letters 2024-07-29 10:58:20 -04:00
Paul Popus
eb191f335b dont uppercase 2024-07-26 13:52:32 -04:00
Paul Popus
02bb176890 dont capitalise 2024-07-26 13:45:18 -04:00
Paul Popus
082c6ef44a add test and cleanup 2024-07-25 18:53:05 -04:00
Paul Popus
f93cdf9364 feat: format the error for field schema 2024-07-25 18:13:27 -04:00
16 changed files with 82 additions and 35 deletions

View File

@@ -6,6 +6,7 @@ import type { Field, FieldHookArgs, TabAsField, ValidateOptions } from '../../co
import { MissingEditorProp } from '../../../errors/index.js'
import { deepMergeWithSourceArrays } from '../../../utilities/deepMerge.js'
import { formatErrorLabels } from '../../../utilities/formatLabels.js'
import { fieldAffectsData, tabHasName } from '../../config/types.js'
import { getFieldPaths } from '../../getFieldPaths.js'
import { beforeDuplicate } from './beforeDuplicate.js'
@@ -146,7 +147,7 @@ export const promise = async ({
if (typeof validationResult === 'string') {
errors.push({
field: fieldPath.join('.'),
field: formatErrorLabels(fieldPath),
message: validationResult,
})
}

View File

@@ -981,7 +981,7 @@ export {
deepMergeWithSourceArrays,
} from './utilities/deepMerge.js'
export { default as flattenTopLevelFields } from './utilities/flattenTopLevelFields.js'
export { formatLabels, formatNames, toWords } from './utilities/formatLabels.js'
export { formatErrorLabels, formatLabels, formatNames, toWords } from './utilities/formatLabels.js'
export { getCollectionIDFieldTypes } from './utilities/getCollectionIDFieldTypes.js'
export { getObjectDotNotation } from './utilities/getObjectDotNotation.js'
export { initTransaction } from './utilities/initTransaction.js'

View File

@@ -1,4 +1,4 @@
import { formatLabels, toWords } from './formatLabels'
import { formatErrorLabels, formatLabels, toWords } from './formatLabels'
describe('formatLabels', () => {
it('should format singular slug', () => {
@@ -39,3 +39,11 @@ describe('formatLabels', () => {
})
})
})
describe('formatErrorLabels', () => {
it('should format array', () => {
expect(formatErrorLabels(['test', 1, 'field', 'array', 3, 'final'])).toBe(
'Test (1) > Field > Array (3) > Final',
)
})
})

View File

@@ -9,7 +9,8 @@ const toWords = (inputString: string, joinWords = false): string => {
const trimmedString = notNullString.trim()
const arrayOfStrings = trimmedString.split(/[\s-]/)
const splitStringsArray = []
const splitStringsArray: string[] = []
arrayOfStrings.forEach((tempString) => {
if (tempString !== '') {
const splitWords = tempString.split(/(?=[A-Z])/).join(' ')
@@ -46,4 +47,23 @@ const formatNames = (slug: string): { plural: string; singular: string } => {
}
}
export { formatLabels, formatNames, toWords }
/**
* Formats labels for error field schema array.
* @param fieldSchema - Array of strings and numbers.
* @returns Formatted string.
*
* @example
* formatErrorLabels(['test', 1, 'test2']) // => 'Test (1) > Test2'
*/
const formatErrorLabels = (fieldSchema: (number | string)[]): string => {
return fieldSchema.reduce((acc: string, current, index) => {
if (typeof current === 'number') {
return index === 0 ? `(${current})` : `${acc} (${current})`
} else {
const formattedLabel = current.charAt(0).toUpperCase() + current.slice(1)
return index === 0 ? `${acc}${formattedLabel}` : `${acc} > ${formattedLabel}`
}
}, '') as string
}
export { formatErrorLabels, formatLabels, formatNames, toWords }

View File

@@ -253,7 +253,9 @@ describe('access control', () => {
await expect(page.locator('#field-name')).toHaveValue('name')
await expect(page.locator('#action-save')).toBeVisible()
await page.locator('#action-save').click()
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully', {
ignoreCase: true,
})
await expect(page.locator('#action-save')).toBeHidden()
await expect(page.locator('#field-name')).toBeDisabled()
})
@@ -280,7 +282,9 @@ describe('access control', () => {
await documentDrawer.locator('#field-name').fill('name')
await expect(documentDrawer.locator('#field-name')).toHaveValue('name')
await documentDrawer.locator('#action-save').click()
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully', {
ignoreCase: true,
})
await expect(documentDrawer.locator('#action-save')).toBeHidden()
await expect(documentDrawer.locator('#field-name')).toBeDisabled()
})
@@ -304,7 +308,9 @@ describe('access control', () => {
await expect(page.locator('#field-name')).toBeVisible()
await page.locator('#field-name').fill('anonymous@email.com')
await page.locator('#action-save').click()
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully', {
ignoreCase: true,
})
await expect(page.locator('#field-name')).toBeDisabled()
await expect(page.locator('#action-save')).toBeHidden()
@@ -312,7 +318,9 @@ describe('access control', () => {
await expect(page.locator('#field-name')).toBeVisible()
await page.locator('#field-name').fill(devUser.email)
await page.locator('#action-save').click()
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully', {
ignoreCase: true,
})
await expect(page.locator('#field-name')).toBeEnabled()
await expect(page.locator('#action-save')).toBeVisible()
})
@@ -335,7 +343,9 @@ describe('access control', () => {
await expect(documentDrawer).toBeVisible()
await documentDrawer.locator('#field-name').fill('anonymous@email.com')
await documentDrawer.locator('#action-save').click()
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully', {
ignoreCase: true,
})
await expect(documentDrawer.locator('#field-name')).toBeDisabled()
await documentDrawer.locator('button.doc-drawer__header-close').click()
await expect(documentDrawer).toBeHidden()
@@ -344,7 +354,9 @@ describe('access control', () => {
await expect(documentDrawer2).toBeVisible()
await documentDrawer2.locator('#field-name').fill('dev@payloadcms.com')
await documentDrawer2.locator('#action-save').click()
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully', {
ignoreCase: true,
})
await expect(documentDrawer2.locator('#field-name')).toBeEnabled()
})
})

View File

@@ -636,8 +636,8 @@ describe('collections-graphql', () => {
// creating twice as many records as we are querying to get a random sample
await mapAsync([...Array(10)], () => {
// setTimeout used to randomize the creation timestamp
setTimeout(async () => {
await payload.create({
setTimeout(() => {
void payload.create({
collection: pointSlug,
data: {
// only randomize longitude to make distance comparison easy
@@ -1150,7 +1150,7 @@ describe('collections-graphql', () => {
})
.then((res) => res.json())
expect(Array.isArray(errors)).toBe(true)
expect(errors[0].message).toEqual('The following field is invalid: min')
expect(errors[0].message).toEqual('The following field is invalid: Min')
expect(typeof errors[0].locations).toBeDefined()
})
@@ -1198,13 +1198,13 @@ describe('collections-graphql', () => {
expect(errors[1].extensions.data.errors[0].field).toEqual('email')
expect(Array.isArray(errors[2].locations)).toEqual(true)
expect(errors[2].message).toEqual('The following field is invalid: email')
expect(errors[2].message).toEqual('The following field is invalid: Email')
expect(errors[2].path[0]).toEqual('test4')
expect(errors[2].extensions.name).toEqual('ValidationError')
expect(errors[2].extensions.data.errors[0].message).toEqual(
'Please enter a valid email address.',
)
expect(errors[2].extensions.data.errors[0].field).toEqual('email')
expect(errors[2].extensions.data.errors[0].field).toEqual('Email')
})
it('should return the minimum allowed information about internal errors', async () => {

View File

@@ -259,7 +259,10 @@ describe('fields - relationship', () => {
await expect(field).toContainText(anotherRelationOneDoc.id)
await wait(2000) // Need to wait form state to come back before clicking save
await page.locator('#action-save').click()
await expect(page.locator('.payload-toast-container')).toContainText(`is invalid: ${fieldName}`)
await expect(page.locator('.payload-toast-container')).toContainText(
`is invalid: ${fieldName}`,
{ ignoreCase: true },
)
filteredField = page.locator(`#field-${fieldName} .react-select`)
await filteredField.click({ delay: 100 })
filteredOptions = filteredField.locator('.rs__option')
@@ -406,7 +409,9 @@ describe('fields - relationship', () => {
await drawerField.fill('Newly created document')
const saveButton = documentDrawer.locator('#action-save')
await saveButton.click()
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully', {
ignoreCase: true,
})
await expect(
page.locator('#field-relationshipHasMany .value-container .rs__multi-value'),
).toHaveCount(1)

View File

@@ -108,7 +108,7 @@ describe('Array', () => {
await page.click('#action-save', { delay: 100 })
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: arrayWithMinRows',
'The following field is invalid: ArrayWithMinRows',
)
})

View File

@@ -224,7 +224,7 @@ describe('Block fields', () => {
await page.click('#action-save', { delay: 100 })
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: blocksWithMinRows',
'The following field is invalid: BlocksWithMinRows',
)
})

View File

@@ -152,7 +152,7 @@ describe('Number', () => {
await page.click('#action-save', { delay: 100 })
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: withMinRows',
'The following field is invalid: WithMinRows',
)
})
})

View File

@@ -374,7 +374,7 @@ describe('relationship', () => {
await page.click('#action-save', { delay: 100 })
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: relationshipWithMinRows',
'The following field is invalid: RelationshipWithMinRows',
)
})

View File

@@ -123,7 +123,7 @@ describe('fields', () => {
// toast error
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: group.unique',
'The following field is invalid: Group.unique',
)
await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).toContain('create')

View File

@@ -405,7 +405,7 @@ describe('Fields', () => {
min: 5,
},
}),
).rejects.toThrow('The following field is invalid: min')
).rejects.toThrow('The following field is invalid: Min')
})
it('should not create number above max', async () => {
await expect(async () =>
@@ -415,7 +415,7 @@ describe('Fields', () => {
max: 15,
},
}),
).rejects.toThrow('The following field is invalid: max')
).rejects.toThrow('The following field is invalid: Max')
})
it('should not create number below 0', async () => {
@@ -426,7 +426,7 @@ describe('Fields', () => {
positiveNumber: -5,
},
}),
).rejects.toThrow('The following field is invalid: positiveNumber')
).rejects.toThrow('The following field is invalid: PositiveNumber')
})
it('should not create number above 0', async () => {
@@ -437,7 +437,7 @@ describe('Fields', () => {
negativeNumber: 5,
},
}),
).rejects.toThrow('The following field is invalid: negativeNumber')
).rejects.toThrow('The following field is invalid: NegativeNumber')
})
it('should not create a decimal number below min', async () => {
await expect(async () =>
@@ -447,7 +447,7 @@ describe('Fields', () => {
decimalMin: -0.25,
},
}),
).rejects.toThrow('The following field is invalid: decimalMin')
).rejects.toThrow('The following field is invalid: DecimalMin')
})
it('should not create a decimal number above max', async () => {
@@ -458,7 +458,7 @@ describe('Fields', () => {
decimalMax: 1.5,
},
}),
).rejects.toThrow('The following field is invalid: decimalMax')
).rejects.toThrow('The following field is invalid: DecimalMax')
})
it('should localize an array of numbers using hasMany', async () => {
const localizedHasMany = [5, 10]
@@ -665,7 +665,7 @@ describe('Fields', () => {
min: 5,
},
}),
).rejects.toThrow('The following field is invalid: min')
).rejects.toThrow('The following field is invalid: Min')
expect(doc.point).toEqual(point)
expect(doc.localized).toEqual(localized)
@@ -1292,7 +1292,7 @@ describe('Fields', () => {
json: '{ bad input: true }',
},
}),
).rejects.toThrow('The following field is invalid: json')
).rejects.toThrow('The following field is invalid: Json')
})
it('should validate json schema', async () => {
@@ -1303,7 +1303,7 @@ describe('Fields', () => {
json: { foo: 'bad' },
},
}),
).rejects.toThrow('The following field is invalid: json')
).rejects.toThrow('The following field is invalid: Json')
})
it('should save empty json objects', async () => {

View File

@@ -449,7 +449,7 @@ describe('Relationships', () => {
// @ts-expect-error Sending bad data to test error handling
customIdRelation: 1234,
}),
).rejects.toThrow('The following field is invalid: customIdRelation')
).rejects.toThrow('The following field is invalid: CustomIdRelation')
})
it('should validate the format of number id relationships', async () => {
@@ -458,7 +458,7 @@ describe('Relationships', () => {
// @ts-expect-error Sending bad data to test error handling
customIdNumberRelation: 'bad-input',
}),
).rejects.toThrow('The following field is invalid: customIdNumberRelation')
).rejects.toThrow('The following field is invalid: CustomIdNumberRelation')
})
it('should allow update removing a relationship', async () => {

View File

@@ -297,6 +297,7 @@ describe('uploads', () => {
await page.locator('button#action-save').click()
await expect(page.locator('.payload-toast-container .toast-error')).toContainText(
'The following field is invalid: audio',
{ ignoreCase: true },
)
})

View File

@@ -511,7 +511,7 @@ describe('Versions', () => {
expect(updateManyResult.docs).toHaveLength(0)
expect(updateManyResult.errors).toStrictEqual([
{ id: doc.id, message: 'The following field is invalid: title' },
{ id: doc.id, message: 'The following field is invalid: Title' },
])
})
})