Files
upstream-twenty/packages/twenty-oxlint-rules/rules/folder-structure.spec.ts
upstream-mirror e7da97194a
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
snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
2026-09-17 23:28:13 +05:00

362 lines
8.3 KiB
TypeScript

import { RuleTester } from 'oxlint/plugins-dev';
import { rule, RULE_NAME } from './folder-structure';
const ruleTester = new RuleTester();
const DUMMY_CODE = 'const x = 1;';
const PROJECT = '/project/packages/twenty-front';
const filename = (path: string) => `${PROJECT}/${path}`;
ruleTester.run(RULE_NAME, rule, {
valid: [
// Files outside src/modules/ are ignored
{
code: DUMMY_CODE,
filename: filename('src/utils/helper.ts'),
},
{
code: DUMMY_CODE,
filename: filename('src/components/Button.tsx'),
},
// types/ root folder allows anything
{
code: DUMMY_CODE,
filename: filename('src/modules/types/global.d.ts'),
},
{
code: DUMMY_CODE,
filename: filename('src/modules/types/nested/deep.ts'),
},
// Hooks: use{PascalCase}.(ts|tsx)
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/hooks/useCreateOneRecord.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/hooks/useMyHook.tsx',
),
},
// Hook tests: use{PascalCase}.(test|spec).(ts|tsx)
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/hooks/__tests__/useCreateOneRecord.test.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/hooks/__tests__/useMyHook.test.tsx',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/hooks/__tests__/useMyHook.spec.tsx',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/hooks/__tests__/__snapshots__/snap.ts',
),
},
// Hooks internal
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/hooks/internal/useInternalHook.ts',
),
},
// Hooks __mocks__
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/hooks/__mocks__/useMyHook.ts',
),
},
// Utils: {camelCase}.(ts|tsx)
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/utils/buildQuery.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/utils/getCardComponent.tsx',
),
},
// Util tests: {camelCase}.(test|spec).(ts|tsx)
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/utils/__tests__/buildQuery.test.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/utils/__tests__/buildQuery.spec.ts',
),
},
// Utils kebab-case subfolders (leaf directories)
{
code: DUMMY_CODE,
filename: filename(
'src/modules/workflow/utils/cron-to-human/parseCron.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/workflow/utils/cron-to-human/types/CronParts.ts',
),
},
// Standard allowed subdirs
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/components/RecordTable.tsx',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/states/someState.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/constants/DEFAULT_VALUE.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/graphql/queries.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/effect-components/MyEffect.tsx',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/validation-schemas/schema.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/contexts/MyContext.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/scopes/MyScope.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/services/recordService.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/errors/NotFoundError.ts',
),
},
// Nested modules up to depth 4
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/sub-module/components/Widget.tsx',
),
},
{
code: DUMMY_CODE,
filename: filename('src/modules/a/b/c/d/hooks/useDeep.ts'),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/a/b/c/d/e/hooks/useDeep.ts',
),
},
// Files directly in module folders
{
code: DUMMY_CODE,
filename: filename('src/modules/my-feature/index.ts'),
},
// __tests__ inside module
{
code: DUMMY_CODE,
filename: filename(
'src/modules/object-record/__tests__/integration.test.ts',
),
},
// Test helpers and non-standard files in __tests__/ are allowed (leaf context)
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/hooks/__tests__/TestWrapper.tsx',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/hooks/__tests__/constants/mockData.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/utils/__tests__/helpers/setup.ts',
),
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/utils/__tests__/kebab-name.test.js',
),
},
],
invalid: [
// Files directly in modules root
{
code: DUMMY_CODE,
filename: filename('src/modules/loose-file.ts'),
errors: [{ messageId: 'noFilesInModulesRoot' }],
},
// Non-kebab-case module names
{
code: DUMMY_CODE,
filename: filename(
'src/modules/MyModule/hooks/useHook.ts',
),
errors: [{ messageId: 'moduleNameNotKebabCase' }],
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/camelCase/hooks/useHook.ts',
),
errors: [{ messageId: 'moduleNameNotKebabCase' }],
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/graphWidgetChart/file.ts',
),
errors: [{ messageId: 'moduleNameNotKebabCase' }],
},
// Module nesting too deep (depth > 5)
{
code: DUMMY_CODE,
filename: filename(
'src/modules/a/b/c/d/e/f/hooks/useDeep.ts',
),
errors: [{ messageId: 'moduleTooDeep' }],
},
// Depth check fires before kebab-case check at max depth
{
code: DUMMY_CODE,
filename: filename(
'src/modules/a/b/c/d/e/camelCaseDir/file.ts',
),
errors: [{ messageId: 'moduleTooDeep' }],
},
// Bad hook file naming
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/hooks/badName.ts',
),
errors: [{ messageId: 'hookFileNaming' }],
},
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/hooks/helper.ts',
),
errors: [{ messageId: 'hookFileNaming' }],
},
// Invalid hooks folder entry
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/hooks/random-folder/file.ts',
),
errors: [{ messageId: 'invalidHooksEntry' }],
},
// Bad util file naming (PascalCase not allowed)
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/utils/BadName.ts',
),
errors: [{ messageId: 'utilFileNaming' }],
},
// Bad util file naming (kebab-case files not allowed)
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/utils/kebab-name.ts',
),
errors: [{ messageId: 'utilFileNaming' }],
},
// Non-kebab-case subfolder in utils
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/utils/camelCase/file.ts',
),
errors: [{ messageId: 'invalidUtilsEntry' }],
},
// hooks/internal/ too deep
{
code: DUMMY_CODE,
filename: filename(
'src/modules/my-feature/hooks/internal/internal/internal/useHook.ts',
),
errors: [{ messageId: 'hooksInternalTooDeep' }],
},
],
});