test: move i18n list tests to i18n suite (#13143)
This commit is contained in:
@@ -1590,55 +1590,6 @@ describe('List View', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('i18n', () => {
|
|
||||||
test('should display translated collections and globals config options', async () => {
|
|
||||||
await page.goto(postsUrl.list)
|
|
||||||
await expect(page.locator('#nav-posts')).toContainText('Posts')
|
|
||||||
await expect(page.locator('#nav-global-global')).toContainText('Global')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should display translated field titles', async () => {
|
|
||||||
await createPost()
|
|
||||||
|
|
||||||
await page.locator('.list-controls__toggle-columns').click()
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.locator('.pill-selector__pill', {
|
|
||||||
hasText: exactText('Title'),
|
|
||||||
}),
|
|
||||||
).toHaveText('Title')
|
|
||||||
|
|
||||||
await openListFilters(page, {})
|
|
||||||
|
|
||||||
await page.locator('.where-builder__add-first-filter').click()
|
|
||||||
await page.locator('.condition__field .rs__control').click()
|
|
||||||
const options = page.locator('.rs__option')
|
|
||||||
|
|
||||||
await expect(options.locator('text=Tab 1 > Title')).toHaveText('Tab 1 > Title')
|
|
||||||
|
|
||||||
await expect(page.locator('#heading-title .sort-column__label')).toHaveText('Title')
|
|
||||||
await expect(page.locator('.search-filter input')).toHaveAttribute('placeholder', /(Title)/)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should use fallback language on field titles', async () => {
|
|
||||||
// change language German
|
|
||||||
await page.goto(postsUrl.account)
|
|
||||||
await page.locator('.payload-settings__language .react-select').click()
|
|
||||||
const languageSelect = page.locator('.rs__option')
|
|
||||||
// text field does not have a 'de' label
|
|
||||||
await languageSelect.locator('text=Deutsch').click()
|
|
||||||
|
|
||||||
await page.goto(postsUrl.list)
|
|
||||||
await page.locator('.list-controls__toggle-columns').click()
|
|
||||||
// expecting the label to fall back to english as default fallbackLng
|
|
||||||
await expect(
|
|
||||||
page.locator('.pill-selector__pill', {
|
|
||||||
hasText: exactText('Title'),
|
|
||||||
}),
|
|
||||||
).toHaveText('Title')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('placeholder', () => {
|
describe('placeholder', () => {
|
||||||
test('should display placeholder in filter options', async () => {
|
test('should display placeholder in filter options', async () => {
|
||||||
await page.goto(
|
await page.goto(
|
||||||
|
|||||||
@@ -45,7 +45,25 @@ export default buildConfigWithDefaults({
|
|||||||
collections: [
|
collections: [
|
||||||
{
|
{
|
||||||
slug: 'collection1',
|
slug: 'collection1',
|
||||||
|
labels: {
|
||||||
|
singular: {
|
||||||
|
en: 'EN Collection 1',
|
||||||
|
es: 'ES Collection 1',
|
||||||
|
},
|
||||||
|
plural: {
|
||||||
|
en: 'EN Collection 1s',
|
||||||
|
es: 'ES Collection 1s',
|
||||||
|
},
|
||||||
|
},
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'i18nFieldLabel',
|
||||||
|
type: 'text',
|
||||||
|
label: {
|
||||||
|
en: 'en-label',
|
||||||
|
es: 'es-label',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'fieldDefaultI18nValid',
|
name: 'fieldDefaultI18nValid',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
@@ -79,6 +97,16 @@ export default buildConfigWithDefaults({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
globals: [
|
||||||
|
{
|
||||||
|
slug: 'global',
|
||||||
|
label: {
|
||||||
|
en: 'EN Global',
|
||||||
|
es: 'ES Global',
|
||||||
|
},
|
||||||
|
fields: [{ name: 'text', type: 'text' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
i18n: {
|
i18n: {
|
||||||
translations: customTranslationsObject,
|
translations: customTranslationsObject,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,10 +9,17 @@ const { beforeAll, beforeEach, describe } = test
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
import type { PayloadTestSDK } from '../helpers/sdk/index.js'
|
||||||
|
|
||||||
import { ensureCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
import { ensureCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||||
|
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||||
|
import { openListFilters } from '../helpers/e2e/openListFilters.js'
|
||||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||||
import { reInitializeDB } from '../helpers/reInitializeDB.js'
|
import { reInitializeDB } from '../helpers/reInitializeDB.js'
|
||||||
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||||
|
|
||||||
|
let payload: PayloadTestSDK<Config>
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
const dirname = path.dirname(filename)
|
const dirname = path.dirname(filename)
|
||||||
|
|
||||||
@@ -20,6 +27,7 @@ describe('i18n', () => {
|
|||||||
let page: Page
|
let page: Page
|
||||||
|
|
||||||
let serverURL: string
|
let serverURL: string
|
||||||
|
let collection1URL: AdminUrlUtil
|
||||||
|
|
||||||
beforeAll(async ({ browser }, testInfo) => {
|
beforeAll(async ({ browser }, testInfo) => {
|
||||||
const prebuild = false // Boolean(process.env.CI)
|
const prebuild = false // Boolean(process.env.CI)
|
||||||
@@ -27,11 +35,13 @@ describe('i18n', () => {
|
|||||||
testInfo.setTimeout(TEST_TIMEOUT_LONG)
|
testInfo.setTimeout(TEST_TIMEOUT_LONG)
|
||||||
|
|
||||||
process.env.SEED_IN_CONFIG_ONINIT = 'false' // Makes it so the payload config onInit seed is not run. Otherwise, the seed would be run unnecessarily twice for the initial test run - once for beforeEach and once for onInit
|
process.env.SEED_IN_CONFIG_ONINIT = 'false' // Makes it so the payload config onInit seed is not run. Otherwise, the seed would be run unnecessarily twice for the initial test run - once for beforeEach and once for onInit
|
||||||
;({ serverURL } = await initPayloadE2ENoConfig<Config>({
|
;({ payload, serverURL } = await initPayloadE2ENoConfig<Config>({
|
||||||
dirname,
|
dirname,
|
||||||
prebuild,
|
prebuild,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
collection1URL = new AdminUrlUtil(serverURL, 'collection1')
|
||||||
|
|
||||||
const context = await browser.newContext()
|
const context = await browser.newContext()
|
||||||
page = await context.newPage()
|
page = await context.newPage()
|
||||||
initPageConsoleErrorCatch(page)
|
initPageConsoleErrorCatch(page)
|
||||||
@@ -47,7 +57,35 @@ describe('i18n', () => {
|
|||||||
await ensureCompilationIsDone({ page, serverURL })
|
await ensureCompilationIsDone({ page, serverURL })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function setUserLanguage(language: 'de' | 'en' | 'es') {
|
||||||
|
{
|
||||||
|
const LanguageLabel = {
|
||||||
|
de: {
|
||||||
|
fieldLabel: 'Sprache',
|
||||||
|
valueLabel: 'Deutsch',
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
fieldLabel: 'Language',
|
||||||
|
valueLabel: 'English',
|
||||||
|
},
|
||||||
|
es: {
|
||||||
|
fieldLabel: 'Idioma',
|
||||||
|
valueLabel: 'Español',
|
||||||
|
},
|
||||||
|
}[language]
|
||||||
|
await page.goto(serverURL + '/admin/account')
|
||||||
|
await page.locator('.payload-settings__language .react-select').click()
|
||||||
|
await page.locator('.rs__option', { hasText: LanguageLabel.valueLabel }).click()
|
||||||
|
await expect(
|
||||||
|
page.locator('.payload-settings__language', { hasText: LanguageLabel.fieldLabel }),
|
||||||
|
).toBeVisible()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test('ensure i18n labels and useTranslation hooks display correct translation', async () => {
|
test('ensure i18n labels and useTranslation hooks display correct translation', async () => {
|
||||||
|
// set language to English
|
||||||
|
await setUserLanguage('en')
|
||||||
|
|
||||||
await page.goto(serverURL + '/admin')
|
await page.goto(serverURL + '/admin')
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@@ -84,10 +122,9 @@ describe('i18n', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('ensure translations update correctly when switching language', async () => {
|
test('ensure translations update correctly when switching language', async () => {
|
||||||
await page.goto(serverURL + '/admin/account')
|
// set language to English
|
||||||
|
await setUserLanguage('en')
|
||||||
|
|
||||||
await page.locator('div.rs__control').click()
|
|
||||||
await page.locator('div.rs__option').filter({ hasText: 'English' }).click()
|
|
||||||
await expect(page.locator('div.payload-settings h3')).toHaveText('Payload Settings')
|
await expect(page.locator('div.payload-settings h3')).toHaveText('Payload Settings')
|
||||||
|
|
||||||
await page.goto(serverURL + '/admin/collections/collection1/create')
|
await page.goto(serverURL + '/admin/collections/collection1/create')
|
||||||
@@ -95,9 +132,8 @@ describe('i18n', () => {
|
|||||||
'Add {{label}}',
|
'Add {{label}}',
|
||||||
)
|
)
|
||||||
|
|
||||||
await page.goto(serverURL + '/admin/account')
|
// set language to Spanish
|
||||||
await page.locator('div.rs__control').click()
|
await setUserLanguage('es')
|
||||||
await page.locator('div.rs__option').filter({ hasText: 'Español' }).click()
|
|
||||||
await expect(page.locator('div.payload-settings h3')).toHaveText('Configuración de Payload')
|
await expect(page.locator('div.payload-settings h3')).toHaveText('Configuración de Payload')
|
||||||
|
|
||||||
await page.goto(serverURL + '/admin/collections/collection1/create')
|
await page.goto(serverURL + '/admin/collections/collection1/create')
|
||||||
@@ -105,4 +141,90 @@ describe('i18n', () => {
|
|||||||
'Añadir {{label}}',
|
'Añadir {{label}}',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('i18n labels', () => {
|
||||||
|
test('should show translated document field label', async () => {
|
||||||
|
// set language to Spanish
|
||||||
|
await setUserLanguage('es')
|
||||||
|
|
||||||
|
await page.goto(collection1URL.create)
|
||||||
|
await expect(
|
||||||
|
page.locator('label[for="field-i18nFieldLabel"]', {
|
||||||
|
hasText: 'es-label',
|
||||||
|
}),
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should show translated pill field label', async () => {
|
||||||
|
// set language to Spanish
|
||||||
|
await setUserLanguage('es')
|
||||||
|
|
||||||
|
await page.goto(collection1URL.list)
|
||||||
|
await page.locator('.list-controls__toggle-columns').click()
|
||||||
|
|
||||||
|
// expecting the label to fall back to english as default fallbackLng
|
||||||
|
await expect(
|
||||||
|
page.locator('.pill-selector__pill', {
|
||||||
|
hasText: 'es-label',
|
||||||
|
}),
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should show fallback pill field label', async () => {
|
||||||
|
// set language to German
|
||||||
|
await setUserLanguage('de')
|
||||||
|
|
||||||
|
await page.goto(collection1URL.list)
|
||||||
|
await page.locator('.list-controls__toggle-columns').click()
|
||||||
|
|
||||||
|
// expecting the label to fall back to english as default fallbackLng
|
||||||
|
await expect(
|
||||||
|
page.locator('.pill-selector__pill', {
|
||||||
|
hasText: 'en-label',
|
||||||
|
}),
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should show translated field label in where builder', async () => {
|
||||||
|
await payload.create({
|
||||||
|
collection: 'collection1',
|
||||||
|
data: {
|
||||||
|
i18nFieldLabel: 'Test',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// set language to Spanish
|
||||||
|
await setUserLanguage('es')
|
||||||
|
|
||||||
|
await page.goto(collection1URL.list)
|
||||||
|
|
||||||
|
await openListFilters(page, {})
|
||||||
|
await page.locator('.where-builder__add-first-filter').click()
|
||||||
|
await page.locator('.condition__field .rs__control').click()
|
||||||
|
|
||||||
|
await expect(page.locator('.rs__option', { hasText: 'es-label' })).toBeVisible()
|
||||||
|
|
||||||
|
// expect heading to be translated
|
||||||
|
await expect(
|
||||||
|
page.locator('#heading-i18nFieldLabel .sort-column__label', { hasText: 'es-label' }),
|
||||||
|
).toBeVisible()
|
||||||
|
await expect(page.locator('.search-filter input')).toHaveAttribute(
|
||||||
|
'placeholder',
|
||||||
|
/(Buscar por ID)/,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should display translated collections and globals config options', async () => {
|
||||||
|
// set language to Spanish
|
||||||
|
await setUserLanguage('es')
|
||||||
|
|
||||||
|
await page.goto(collection1URL.list)
|
||||||
|
await expect(
|
||||||
|
page.locator('#nav-collection1', {
|
||||||
|
hasText: 'ES Collection 1s',
|
||||||
|
}),
|
||||||
|
).toBeVisible()
|
||||||
|
await expect(page.locator('#nav-global-global')).toContainText('ES Global')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -84,8 +84,12 @@ export interface Config {
|
|||||||
db: {
|
db: {
|
||||||
defaultIDType: string;
|
defaultIDType: string;
|
||||||
};
|
};
|
||||||
globals: {};
|
globals: {
|
||||||
globalsSelect: {};
|
global: Global;
|
||||||
|
};
|
||||||
|
globalsSelect: {
|
||||||
|
global: GlobalSelect<false> | GlobalSelect<true>;
|
||||||
|
};
|
||||||
locale: null;
|
locale: null;
|
||||||
user: User & {
|
user: User & {
|
||||||
collection: 'users';
|
collection: 'users';
|
||||||
@@ -119,6 +123,7 @@ export interface UserAuthOperations {
|
|||||||
*/
|
*/
|
||||||
export interface Collection1 {
|
export interface Collection1 {
|
||||||
id: string;
|
id: string;
|
||||||
|
i18nFieldLabel?: string | null;
|
||||||
fieldDefaultI18nValid?: string | null;
|
fieldDefaultI18nValid?: string | null;
|
||||||
fieldDefaultI18nInvalid?: string | null;
|
fieldDefaultI18nInvalid?: string | null;
|
||||||
fieldCustomI18nValidDefault?: string | null;
|
fieldCustomI18nValidDefault?: string | null;
|
||||||
@@ -142,6 +147,13 @@ export interface User {
|
|||||||
hash?: string | null;
|
hash?: string | null;
|
||||||
loginAttempts?: number | null;
|
loginAttempts?: number | null;
|
||||||
lockUntil?: string | null;
|
lockUntil?: string | null;
|
||||||
|
sessions?:
|
||||||
|
| {
|
||||||
|
id: string;
|
||||||
|
createdAt?: string | null;
|
||||||
|
expiresAt: string;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
password?: string | null;
|
password?: string | null;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -206,6 +218,7 @@ export interface PayloadMigration {
|
|||||||
* via the `definition` "collection1_select".
|
* via the `definition` "collection1_select".
|
||||||
*/
|
*/
|
||||||
export interface Collection1Select<T extends boolean = true> {
|
export interface Collection1Select<T extends boolean = true> {
|
||||||
|
i18nFieldLabel?: T;
|
||||||
fieldDefaultI18nValid?: T;
|
fieldDefaultI18nValid?: T;
|
||||||
fieldDefaultI18nInvalid?: T;
|
fieldDefaultI18nInvalid?: T;
|
||||||
fieldCustomI18nValidDefault?: T;
|
fieldCustomI18nValidDefault?: T;
|
||||||
@@ -228,6 +241,13 @@ export interface UsersSelect<T extends boolean = true> {
|
|||||||
hash?: T;
|
hash?: T;
|
||||||
loginAttempts?: T;
|
loginAttempts?: T;
|
||||||
lockUntil?: T;
|
lockUntil?: T;
|
||||||
|
sessions?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
id?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
expiresAt?: T;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
@@ -261,6 +281,26 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
|
|||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "global".
|
||||||
|
*/
|
||||||
|
export interface Global {
|
||||||
|
id: string;
|
||||||
|
text?: string | null;
|
||||||
|
updatedAt?: string | null;
|
||||||
|
createdAt?: string | null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "global_select".
|
||||||
|
*/
|
||||||
|
export interface GlobalSelect<T extends boolean = true> {
|
||||||
|
text?: T;
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
globalType?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "auth".
|
* via the `definition` "auth".
|
||||||
|
|||||||
Reference in New Issue
Block a user