Files
orca/config/scripts/pr-plugin-e2e-routing.test.mjs
T

167 lines
8.1 KiB
JavaScript

import { describe, expect, it } from 'vitest'
import { existsSync, readdirSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { classifyE2eJobs, generalE2eShards } from './ci-e2e-job-selection.mjs'
import { testImportClosure } from './e2e-test-import-closure.mjs'
import { selectPrE2eSpecs } from './pr-e2e-source-routing.mjs'
import {
PLUGIN_SYSTEM_SOURCE,
PR_PLUGIN_E2E_SOURCE_ROUTES
} from './pr-plugin-e2e-source-routes.mjs'
const projectDir = resolve(import.meta.dirname, '../..')
function repoFiles(directory) {
return readdirSync(join(projectDir, directory), { recursive: true, withFileTypes: true })
.filter((entry) => entry.isFile())
.map((entry) => join(entry.parentPath, entry.name).slice(projectDir.length + 1))
.map((file) => file.split('\\').join('/'))
}
const pluginSources = [
...repoFiles('src/renderer/src/components/plugins'),
...repoFiles('src/main/plugins'),
...repoFiles('src/shared/plugins'),
...readdirSync(join(projectDir, 'src/renderer/src/store'))
.filter((name) => name.startsWith('plugin-'))
.map((name) => `src/renderer/src/store/${name}`)
].filter(
(file) =>
/\.(?:ts|tsx|cjs)$/.test(file) && !/\.test\.tsx?$/.test(file) && !file.includes('/__mocks__/')
)
const pluginSpecs = readdirSync(join(projectDir, 'tests/e2e'))
.filter((name) => /^plugin-.*\.spec\.ts$/.test(name))
.map((name) => `tests/e2e/${name}`)
const routedPluginSpecs = (file) =>
PR_PLUGIN_E2E_SOURCE_ROUTES.filter((route) => route.matches(file)).flatMap((route) => route.specs)
describe('plugin PR E2E routing', () => {
it('runs the update review specs for the #36218 change set', () => {
const specs = selectPrE2eSpecs([
'src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.tsx',
'src/renderer/src/components/plugins/list/PluginsAlphaNotice.tsx',
'src/renderer/src/components/plugins/page/PluginDetailPage.tsx',
'src/renderer/src/components/plugins/presentation/plugin-publisher-trust.ts',
'src/renderer/src/components/plugins/review/PluginUpdateReview.tsx',
'src/renderer/src/components/plugins/state/use-plugin-page-model.ts'
])
expect(specs).toContain('tests/e2e/plugin-update-review.spec.ts')
expect(specs).toContain('tests/e2e/plugin-update-review-toast.spec.ts')
expect(classifyE2eJobs(JSON.stringify(specs)).e2e_run_changed).toBe(true)
})
it.each([
['src/renderer/src/components/plugins/review/PluginInstallConsent.tsx', 'plugin-update-review'],
['src/renderer/src/components/plugins/catalog/PluginCardAction.tsx', 'plugin-install-link'],
['src/renderer/src/components/plugins/state/plugin-try-action.ts', 'plugin-try-it'],
['src/renderer/src/store/plugin-themes.ts', 'plugin-theme-smoke'],
['src/renderer/src/store/plugin-syntax-packs.ts', 'plugin-syntax-smoke'],
['src/renderer/src/store/plugin-custom-editors.ts', 'plugin-custom-editor'],
['src/main/plugins/panels/plugin-panel-controller.ts', 'plugin-panel-containment'],
['src/main/plugins/catalog/plugin-file-type-suggestion.ts', 'plugin-file-type-suggestion'],
['src/main/plugins/catalog/plugin-auto-updater.ts', 'plugin-update-review-toast'],
['src/shared/plugins/manifest/plugin-manifest.ts', 'plugin-demo'],
['src/main/plugins/panels/plugin-frame-module-transform.ts', 'plugin-buildless-tsx'],
['src/cli/plugin-authoring/plugin-type-files.ts', 'plugin-buildless-tsx'],
['examples/plugins/hello-tsx/src/panel.tsx', 'plugin-buildless-tsx'],
[
'src/shared/plugins/declarative/runtime/declarative-memory-retention.ts',
'plugin-components-hot-reload'
],
[
'src/renderer/src/components/right-sidebar/PluginDeclarativePanelNotices.tsx',
'plugin-components-hot-reload'
],
['src/main/plugins/plugin-dev-watcher.ts', 'plugin-components-hot-reload'],
['resources/plugins/declarative/orca-runtime.mjs', 'plugin-components-hot-reload'],
['src/renderer/src/components/plugins/plugin-icon-glyphs.ts', 'plugin-declarative-panel'],
['src/main/plugins/view/plugin-view-capture.ts', 'plugin-shot'],
['src/main/plugins/view/plugin-view-editor-shot.ts', 'plugin-shot-editor'],
['src/renderer/src/store/plugin-shot-replay.ts', 'plugin-shot'],
['src/main/plugins/testing/plugin-test-register.ts', 'plugin-shot'],
['src/renderer/src/components/plugins/view/plugin-view-request-handler.ts', 'plugin-shot'],
['src/renderer/src/components/right-sidebar/plugin-panel-draw-status.ts', 'plugin-shot'],
['src/main/runtime/rpc/methods/plugin-view.ts', 'plugin-shot'],
['src/cli/handlers/plugin-view.ts', 'plugin-shot'],
['src/cli/handlers/plugin-shot-default-dir.ts', 'plugin-shot'],
['src/cli/handlers/plugin-shot-default-dir.ts', 'plugin-shot-editor'],
['src/cli/handlers/plugin-shot-chrome.ts', 'plugin-shot-editor'],
['src/cli/handlers/plugin-shot-editor.ts', 'plugin-shot-editor'],
['src/cli/handlers/plugin-shot-scenario.ts', 'plugin-shot'],
['src/cli/plugin-authoring/plugin-panel-check.ts', 'plugin-shot'],
['src/cli/plugin-authoring/plugin-test-runner.ts', 'plugin-shot'],
['src/renderer/src/store/plugin-view-capture.ts', 'plugin-shot'],
['examples/plugins/hello-orca/main.mjs', 'plugin-demo'],
['examples/plugins/hello-worker-panel/worker/main.mjs', 'plugin-worker-panel'],
['src/main/plugins/panels/plugin-panel-bridge.ts', 'plugin-worker-panel'],
['src/main/plugins/plugin-runtime-binding.ts', 'plugin-panel-agents'],
['examples/plugins/test-fixtures/hostile-panel/panel.html', 'plugin-panel-containment']
])('routes %s to %s', (file, spec) => {
expect(selectPrE2eSpecs([file])).toContain(`tests/e2e/${spec}.spec.ts`)
})
// Editor shots never take the --scenario branch, so replay sources stay off the editor journey.
it.each([
['src/renderer/src/store/plugin-shot-replay.ts', 'plugin-shot-editor'],
['src/main/plugins/view/plugin-view-replay-shot.ts', 'plugin-shot-editor'],
['src/cli/handlers/plugin-shot-scenario.ts', 'plugin-shot-editor'],
['src/cli/handlers/plugin-shot-editor.ts', 'plugin-shot']
])('does not route %s to %s', (file, spec) => {
expect(selectPrE2eSpecs([file])).not.toContain(`tests/e2e/${spec}.spec.ts`)
})
it('routes every plugin product source to at least one plugin spec', () => {
expect(pluginSources.length).toBeGreaterThan(100)
for (const file of pluginSources) {
expect(routedPluginSpecs(file).length, file).toBeGreaterThan(0)
}
expect(routedPluginSpecs('src/main/plugins/plugin-future-feature.ts')).toEqual([
'tests/e2e/plugin-demo.spec.ts'
])
})
it('gives every plugin spec a source route, and every routed spec exists', () => {
const routed = new Set(PR_PLUGIN_E2E_SOURCE_ROUTES.flatMap((route) => route.specs))
for (const spec of routed) {
expect(existsSync(join(projectDir, spec)), spec).toBe(true)
}
expect([...routed].sort()).toEqual(pluginSpecs.sort())
})
it('routes each plugin spec from every plugin helper it imports', () => {
for (const spec of pluginSpecs) {
const helpers = testImportClosure(projectDir, [spec]).filter((file) =>
file.startsWith('tests/e2e/helpers/plugin-')
)
for (const helper of helpers) {
expect(selectPrE2eSpecs([helper]), `${helper} -> ${spec}`).toContain(spec)
}
}
})
it('keeps plugin selections in one changed-spec shard', () => {
const everything = selectPrE2eSpecs(
pluginSources.filter((file) => PLUGIN_SYSTEM_SOURCE.test(file))
)
expect(everything.filter((spec) => !pluginSpecs.includes(spec))).toEqual([])
expect(generalE2eShards(JSON.stringify(everything))).toEqual(['1/1'])
for (const file of pluginSources) {
expect(new Set(routedPluginSpecs(file)).size, file).toBeLessThanOrEqual(4)
}
})
it('leaves unit tests and unrelated sources alone', () => {
for (const file of [
'src/renderer/src/components/plugins/review/PluginUpdateReview.test.tsx',
'src/main/plugins/plugin-service.test.ts',
'src/main/plugins/__mocks__/plugin-git-ssh-remote-fixture.ts',
'src/renderer/src/components/settings/general/GeneralPane.tsx',
'src/main/ipc/filesystem.ts'
]) {
expect(routedPluginSpecs(file), file).toEqual([])
}
})
})