Files
twenty/packages/twenty-e2e-testing/tests/create-kanban-view.spec.ts
T
Charles BochetandGitHub 1109b89cd7 fix: use Redis metadata version for GraphQL response cache key (#19111)
## Summary

- Fix stale `ObjectMetadataItems` GraphQL response cache after field
creation by using `request.workspaceMetadataVersion` (sourced from
Redis) instead of `workspace.metadataVersion` (from the potentially
stale CoreEntityCacheService)
- Make the E2E kanban view test selector more robust with a regex match

## Root Cause

The `useCachedMetadata` GraphQL plugin keys cached responses using
`workspace.metadataVersion` from the `CoreEntityCacheService`. When a
field is created:

1. The migration runner increments `metadataVersion` in DB and Redis
2. But the `CoreEntityCacheService` for `WorkspaceEntity` is **not**
invalidated
3. So `request.workspace.metadataVersion` still has the old version
4. The cache key resolves to the old cached response
5. The frontend gets stale metadata without the newly created field

This breaks E2E tests (and likely affects users) - after creating a
custom field, the metadata isn't visible until the workspace entity
cache refreshes.

## Fix

Use `request.workspaceMetadataVersion` (populated from Redis by the
middleware, always up-to-date) as the primary version for cache keys,
falling back to the entity cache version.

## Test plan

- [ ] E2E `create-kanban-view` tests should pass (creating a Select
field and immediately using it in a Kanban view)
- [ ] Verify `ObjectMetadataItems` returns fresh data after field
creation (no stale cache)


Made with [Cursor](https://cursor.com)
2026-03-30 15:01:54 +02:00

55 lines
2.9 KiB
TypeScript

import { expect, test } from '../lib/fixtures/screenshot';
test.describe.serial('Create Kanban View', () => {
test('Create Industry Select Field', async ({ page }) => {
await page.getByRole('button', { name: 'Settings' }).click();
await page.getByRole('link', { name: 'Data model' }).click();
await page.getByRole('link', { name: 'Opportunities' }).click();
await expect(page.getByRole('button', { name: 'New Field' })).toBeVisible();
await page.getByRole('button', { name: 'New Field' }).click();
await page.getByRole('link', { name: 'Select', exact: true }).click();
await page.getByRole('textbox', { name: 'Employees' }).click();
await page.getByRole('textbox', { name: 'Employees' }).fill('Industry');
await page.getByRole('textbox').nth(1).click();
await page.getByRole('textbox').nth(1).press('ControlOrMeta+a');
await page.getByRole('textbox').nth(1).fill('Food');
await page.getByRole('button', { name: 'Add option' }).click();
await page.getByRole('button', { name: 'Option 2' }).getByRole('textbox').fill('Tech');
await page.getByRole('button', { name: 'Add option' }).click();
await page.getByRole('button', { name: 'Option 3' }).getByRole('textbox').fill('Travel');
await page.getByRole('button', { name: 'Save' }).click();
await page.waitForURL('**/objects/opportunities');
await page.waitForSelector('text=Industry');
await expect(page.getByText('Industry')).toBeVisible();
});
test('Create Kanban View from Industry Select Field', async ({ page }) => {
await page.getByRole('link', { name: 'Opportunities' }).click();
await page.getByRole('button', { name: /All Opportunities/ }).click();
await page.getByText('Add view').click();
await page.getByRole('textbox').press('ControlOrMeta+a');
await page.getByRole('textbox').fill('By industry');
await page.getByRole('button', { name: 'Table', exact: true }).click();
await page.getByText('Kanban').click();
await page.locator('[aria-controls="view-picker-kanban-field-options"]').click();
await page.getByRole('option', { name: 'Industry' }).click();
await page.getByRole('button', { name: 'Create new view' }).click();
await expect(page.getByText('Food')).toBeVisible({ timeout: 30000 });
await expect(page.getByText('Tech')).toBeVisible();
await expect(page.getByText('Travel')).toBeVisible();
await expect(page.getByText('No Value')).toBeVisible();
const byIndustryElements = await page.locator('text=By industry').all();
expect(byIndustryElements.length).toBeGreaterThanOrEqual(1);
for (const element of byIndustryElements) {
await expect(element).toBeVisible();
}
await page.getByText('Options').click();
await page.getByText('Group', { exact: true }).click();
await Promise.all([page.getByTestId('hide-group-').click(),
page.waitForRequest((req) => {
return req.url().includes('/metadata') &&
req.method() === 'POST';
})]);
await expect(page.getByText('No Value')).not.toBeVisible();
});
})