snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
import { describe, expect, it } from 'vitest';
import { checkStoryEmbeds } from '../check-story-embeds.mjs';
const storyIds = new Set(['ui-input-input--default']);
describe('checkStoryEmbeds', () => {
it('accepts an existing story inside a nested preview', () => {
expect(
checkStoryEmbeds({
content: `<Tabs>
<Tab title="Preview">
<StoryEmbed storyId="ui-input-input--default" />
</Tab>
</Tabs>`,
storyIds,
}),
).toEqual([]);
});
it('reports a renamed or deleted story with its line number', () => {
expect(
checkStoryEmbeds({
content: '\n<StoryEmbed storyId="ui-input-input--removed" />',
storyIds,
}),
).toEqual([
'Line 2: Storybook story "ui-input-input--removed" does not exist.',
]);
});
it.each([
'<StoryEmbed />',
'<StoryEmbed storyId={selectedStory} />',
'<StoryEmbed storyId="" />',
'<StoryEmbed storyId="ui-input-input--default" {...props} />',
])('rejects an unverifiable story ID in %s', (content) => {
expect(checkStoryEmbeds({ content, storyIds })).toEqual([
'Line 1: StoryEmbed needs a literal storyId.',
]);
});
it('checks a renamed snippet import', () => {
expect(
checkStoryEmbeds({
content: `import { StoryEmbed as Preview } from '/snippets/ui/StoryEmbed.mdx';
<Preview storyId="removed-story" />`,
storyIds,
}),
).toEqual(['Line 3: Storybook story "removed-story" does not exist.']);
});
it('ignores examples and comments while parsing page metadata', () => {
expect(
checkStoryEmbeds({
content: `---
title: Preview
description: "A {component} preview"
---
\`\`\`mdx
<StoryEmbed storyId="not-a-live-embed" />
\`\`\`
\`<StoryEmbed storyId="inline-code" />\`
{/* <StoryEmbed storyId="commented-out" /> */}`,
storyIds,
}),
).toEqual([]);
});
});

View File

@@ -0,0 +1,48 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { checkUiReferenceImports } from '../check-ui-reference-imports';
describe('UI reference imports', () => {
let pagesDirectory: string;
beforeEach(() => {
pagesDirectory = mkdtempSync(join(tmpdir(), 'twenty-ui-pages-test-'));
mkdirSync(join(pagesDirectory, 'components/input'), { recursive: true });
writeFileSync(
join(pagesDirectory, 'components/input/checkbox.mdx'),
"---\ntitle: Checkbox\n---\n\nimport CheckboxReference from '/snippets/ui/generated/input/checkbox.mdx';\n\n<CheckboxReference />\n",
);
writeFileSync(
join(pagesDirectory, 'tokens.mdx'),
"import TokenReference from '/snippets/ui/generated/tokens.mdx'\n",
);
});
afterEach(() => {
rmSync(pagesDirectory, { recursive: true, force: true });
});
it('passes when every generated reference is imported by a page', () => {
expect(
checkUiReferenceImports({
pagesDirectory,
outputs: [{ name: 'input/checkbox.mdx' }, { name: 'tokens.mdx' }],
}),
).toEqual([]);
});
it('reports pages importing missing references and references no page imports', () => {
expect(
checkUiReferenceImports({
pagesDirectory,
outputs: [{ name: 'input/switch.mdx' }, { name: 'tokens.mdx' }],
}),
).toEqual([
'Missing UI reference: components/input/checkbox.mdx imports input/checkbox.mdx, which is not generated.',
'Unused UI reference: input/switch.mdx is not imported by any page.',
]);
});
});

View File

@@ -0,0 +1,54 @@
import { describe, expect, it } from 'vitest';
import { normalizeDocumentationDefaultValue } from '../../../../twenty-ui/docs/normalizeDocumentationDefaultValue';
import { renderComponentReference } from '../render-component-reference';
describe('documentation defaults', () => {
it.each([
["'onSubmit'", 'onSubmit'],
['"horizontal"', 'horizontal'],
["'it\\'s ready'", "it's ready"],
['"undefined"', 'undefined'],
["''", ''],
['accent', 'accent'],
[false, 'false'],
[0, '0'],
[null, 'null'],
[undefined, null],
['undefined', null],
])('normalizes %j to %j', (value, expected) => {
expect(normalizeDocumentationDefaultValue({ value })).toBe(expected);
});
it('omits absent defaults and renders string defaults without display quotes', () => {
const reference = renderComponentReference({
name: 'Example',
entryPoint: 'twenty-ui/primitives/input',
slug: 'input/example',
props: [
{
name: 'mode',
type: 'string',
required: false,
defaultValue: normalizeDocumentationDefaultValue({
value: "'onSubmit'",
}),
description: '',
},
{
name: 'checked',
type: 'boolean',
required: false,
defaultValue: normalizeDocumentationDefaultValue({
value: 'undefined',
}),
description: '',
},
],
});
expect(normalizeDocumentationDefaultValue(null)).toBeNull();
expect(reference).toContain('default="onSubmit"');
expect(reference.match(/default=/g)).toHaveLength(1);
});
});

View File

@@ -0,0 +1,48 @@
import { describe, expect, it } from 'vitest';
import { extractDocumentationExamples } from '../../../../twenty-ui/docs/extractDocumentationExamples';
describe('documentation examples', () => {
it('extracts fences with info strings, indentation, and other fence markers', () => {
const examples = extractDocumentationExamples(
[
'```tsx App.tsx',
'const app = 1;',
'```',
'<Tabs>',
' ```TSX title="Indented"',
' const indented = 2;',
' ```',
'</Tabs>',
'~~~ts',
'const tilde = 3;',
'~~~',
'````tsx',
'```',
'const nested = 4;',
'````',
'```bash',
'yarn add twenty-ui',
'```',
'```css',
'.summary {}',
'```',
].join('\n'),
);
expect(examples).toEqual([
{ language: 'tsx', code: 'const app = 1;\n' },
{ language: 'tsx', code: 'const indented = 2;\n' },
{ language: 'ts', code: 'const tilde = 3;\n' },
{ language: 'tsx', code: '```\nconst nested = 4;\n' },
]);
});
it('handles CRLF line endings and ignores an unterminated fence', () => {
expect(
extractDocumentationExamples(
'```tsx\r\nconst app = 1;\r\n```\r\n```tsx\r\nconst open = 2;\r\n',
),
).toEqual([{ language: 'tsx', code: 'const app = 1;\n' }]);
});
});

View File

@@ -0,0 +1,66 @@
import ts from 'typescript';
import { describe, expect, it } from 'vitest';
import { getDocumentationImportDiagnostics } from '../../../../twenty-ui/docs/getDocumentationImportDiagnostics';
const allowedModules = new Set([
'twenty-ui',
'twenty-ui/primitives/input',
'twenty-ui/style.css',
'react',
]);
const checkImports = (content: string) =>
getDocumentationImportDiagnostics({
source: ts.createSourceFile(
'example.tsx',
content,
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TSX,
),
allowedModules,
});
describe('documentation imports', () => {
it('allows public entry points, exported CSS, and peer dependencies', () => {
expect(
checkImports(`
import { Input } from 'twenty-ui/primitives/input';
import 'twenty-ui/style.css';
export { Text } from 'twenty-ui';
import { useState } from 'react';
`),
).toEqual([]);
});
it.each([
"import { Internal } from 'twenty-ui/internal';",
"import 'twenty-ui/private.css';",
"export { Internal } from 'twenty-ui/internal';",
"const component = import('twenty-ui/internal');",
"type Internal = import('twenty-ui/internal').Internal;",
"const component = require('twenty-ui/internal');",
])('rejects an unpublished subpath in %s', (content) => {
const diagnostics = checkImports(content);
expect(diagnostics).toHaveLength(1);
expect(diagnostics[0].category).toBe(ts.DiagnosticCategory.Error);
expect(diagnostics[0].messageText).toContain(
'is not exported by twenty-ui/package.json',
);
});
it.each([
"import { Checkbox } from '@ui/primitives/input/Checkbox/Checkbox';",
"import { Input } from '../../twenty-ui/src/input';",
"import { vi } from 'vitest';",
])('rejects a module outside the public surface in %s', (content) => {
const diagnostics = checkImports(content);
expect(diagnostics).toHaveLength(1);
expect(diagnostics[0].messageText).toContain(
'is not a twenty-ui entry point or peer dependency',
);
});
});

View File

@@ -0,0 +1,53 @@
import { describe, expect, it } from 'vitest';
import { normalizeDocumentationPropType } from '../../../../twenty-ui/docs/normalizeDocumentationPropType';
describe('documentation prop types', () => {
it.each([
[
{
type: {
name: 'string | ((state: State) => string | undefined) | undefined',
},
required: false,
},
'string | ((state: State) => string | undefined)',
],
[
{
type: {
name: '((details: { trigger: Element | undefined; }) => void) | undefined',
},
required: false,
},
'((details: { trigger: Element | undefined; }) => void)',
],
[
{ type: { name: 'Element | undefined' }, required: true },
'Element | undefined',
],
[{ type: { name: 'boolean' }, required: false }, 'boolean'],
[
{
type: {
name: 'enum',
value: [{ value: '"sm"' }, { value: '"md"' }, { value: 'undefined' }],
},
required: false,
},
'"sm" | "md"',
],
[
{
type: {
name: 'enum',
value: [{ value: '"sm"' }, { value: 'undefined' }],
},
required: true,
},
'"sm" | undefined',
],
])('normalizes %j to %j', (input, expected) => {
expect(normalizeDocumentationPropType(input)).toBe(expected);
});
});

View File

@@ -0,0 +1,20 @@
import { describe, expect, it } from 'vitest';
import { formatDocumentationTokenValue } from '../../../../twenty-ui/docs/formatDocumentationTokenValue';
describe('documentation token values', () => {
it.each([
[
'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIYAAABQCA), url(data:image/jpeg;base64,/9j/4AAQ)',
'url(data:image/png;base64,...), url(data:image/jpeg;base64,...)',
],
[
'color(display-p3 0.992 0.992 0.996)',
'color(display-p3 0.992 0.992 0.996)',
],
['url(/noise.png)', 'url(/noise.png)'],
['16', '16'],
])('formats %s as %s', (value, expected) => {
expect(formatDocumentationTokenValue(value)).toBe(expected);
});
});

View File

@@ -0,0 +1,156 @@
import { describe, expect, it } from 'vitest';
import { renderComponentReference } from '../render-component-reference';
import { renderTokenReference } from '../render-ui-reference';
describe('renderComponentReference', () => {
it('escapes attributes and prose while keeping markdown and code intact', () => {
const reference = renderComponentReference({
name: 'Example',
entryPoint: 'twenty-ui/primitives/input',
slug: 'input/example',
props: [
{
name: 'render',
type: 'Ref<custom> | "quoted"',
required: true,
defaultValue: null,
description:
'Render `<custom>` with {value} & more.\n\n- `unmount`: Manually unmounts.\n\n```tsx\n<Example render={<span />} />\n```',
},
],
});
expect(reference).toBe(
[
'<ParamField body="render" type="Ref&lt;custom&gt; | &quot;quoted&quot;" required>',
' Render `<custom>` with &#123;value&#125; &amp; more.',
'',
' - `unmount`: Manually unmounts.',
'',
' ```tsx',
' <Example render={<span />} />',
' ```',
'</ParamField>',
'',
].join('\n'),
);
});
});
describe('compound component references', () => {
it('includes convenience props before compound parts', () => {
const reference = renderComponentReference({
name: 'Tooltip',
entryPoint: 'twenty-ui/primitives/surfaces',
slug: 'surfaces/tooltip',
props: [
{
name: 'content',
type: 'ReactNode',
required: true,
defaultValue: null,
description: 'Tooltip content.',
},
],
parts: [
{
name: 'Popup',
props: [
{
name: 'arrow',
type: 'boolean',
required: false,
defaultValue: 'false',
description: 'Show an arrow.',
},
],
},
],
});
expect(reference).toContain('<ParamField body="content"');
expect(reference).toContain('### Tooltip.Popup');
expect(reference).toContain('<ParamField body="Popup.arrow"');
expect(reference.indexOf('body="content"')).toBeLessThan(
reference.indexOf('### Tooltip.Popup'),
);
});
it('distinguishes identically named props on different parts and retains defaults', () => {
const reference = renderComponentReference({
name: 'Menu',
entryPoint: 'twenty-ui/primitives/surfaces',
slug: 'surfaces/menu',
props: [],
parts: [
{
name: 'Item',
props: [
{
name: 'closeOnClick',
type: 'boolean',
required: false,
defaultValue: 'true',
description: 'Close after selection.',
},
],
},
{
name: 'CheckboxItem',
props: [
{
name: 'closeOnClick',
type: 'boolean',
required: false,
defaultValue: 'false',
description: 'Keep open for multiple choices.',
},
],
},
],
});
expect(reference).toContain('### Menu.Item');
expect(reference).toContain('### Menu.CheckboxItem');
expect(reference).toContain(
'<ParamField body="Item.closeOnClick" type="boolean" default="true">',
);
expect(reference).toContain(
'<ParamField body="CheckboxItem.closeOnClick" type="boolean" default="false">',
);
});
});
describe('renderTokenReference', () => {
it('keeps token expressions inside one table cell and distinguishes numeric values', () => {
const reference = renderTokenReference([
{
path: 'spacing.1',
cssVariable: '--t-spacing-1',
light: 'calc(1px | <custom> & {value})\nnext',
dark: '4px',
isNumber: false,
},
{
path: 'icon.size.md',
cssVariable: '--t-icon-size-md',
light: '16',
dark: '16',
isNumber: true,
},
]);
const rows = reference
.split('\n')
.filter(
(line) => line.startsWith('| spacing.') || line.startsWith('| icon.'),
);
expect(rows).toHaveLength(2);
expect(rows.every((row) => row.split('|').length === 7)).toBe(true);
expect(reference).not.toContain('<custom>');
expect(rows[1]).toContain('| Yes |');
expect(reference).toContain('## spacing');
expect(reference).toContain('## icon');
});
});

View File

@@ -0,0 +1,71 @@
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { syncUiReferenceFiles } from '../sync-ui-reference-files';
describe('generated UI reference files', () => {
let directory: string;
const outputs = [
{ name: 'input/current.mdx', content: 'Current reference\n' },
];
beforeEach(() => {
directory = mkdtempSync(join(tmpdir(), 'twenty-ui-reference-test-'));
mkdirSync(join(directory, 'input'));
writeFileSync(join(directory, 'input/obsolete.mdx'), 'Old reference\n');
});
afterEach(() => {
rmSync(directory, { recursive: true, force: true });
});
it('rejects obsolete and missing references without changing files in check mode', () => {
expect(
syncUiReferenceFiles({ directory, outputs, isCheckMode: true }),
).toEqual([
'Obsolete UI reference: input/obsolete.mdx',
'Stale UI reference: input/current.mdx',
]);
expect(readFileSync(join(directory, 'input/obsolete.mdx'), 'utf8')).toBe(
'Old reference\n',
);
expect(existsSync(join(directory, 'input/current.mdx'))).toBe(false);
});
it('removes obsolete snippets, writes current outputs, and passes a subsequent check', () => {
writeFileSync(join(directory, 'metadata.json'), '{}');
expect(
syncUiReferenceFiles({ directory, outputs, isCheckMode: false }),
).toEqual([]);
expect(existsSync(join(directory, 'input/obsolete.mdx'))).toBe(false);
expect(readFileSync(join(directory, 'input/current.mdx'), 'utf8')).toBe(
outputs[0].content,
);
expect(existsSync(join(directory, 'metadata.json'))).toBe(true);
expect(
syncUiReferenceFiles({ directory, outputs, isCheckMode: true }),
).toEqual([]);
});
it('reports changed content without overwriting it in check mode', () => {
rmSync(join(directory, 'input/obsolete.mdx'));
writeFileSync(join(directory, 'input/current.mdx'), 'Outdated reference\n');
expect(
syncUiReferenceFiles({ directory, outputs, isCheckMode: true }),
).toEqual(['Stale UI reference: input/current.mdx']);
expect(readFileSync(join(directory, 'input/current.mdx'), 'utf8')).toBe(
'Outdated reference\n',
);
});
});