Files
webstudio/apps/builder/e2e/test-modules.ts
Oleg Isonen 0070fa66a1 ci: deduplicate builder E2E setup (#5917)
## What changed

- reuse the shared CI setup action in both Builder E2E jobs
- use one build command for CI and local E2E runs
- centralize shard filename parsing and suite-name formatting
- remove redundant suite-level shard filtering
- parse each E2E filename's shard metadata once

## Why

The merged image-asset PR introduced multiple owners for Builder E2E
setup, build dependencies, and shard filename semantics. Consolidating
them prevents CI and local behavior from drifting as the shard layout
evolves.

## Validation

- `pnpm --filter=@webstudio-is/builder test -- e2e/test-modules.test.ts`
- `pnpm --filter=@webstudio-is/builder typecheck`
- `pnpm lint`
- `pnpm e2e:builder:build`
- shell syntax and YAML parsing
- shard discovery returned shards 1–6
2026-07-18 10:43:56 +02:00

64 lines
2.0 KiB
TypeScript

const e2eFileSuffix = ".e2e.ts";
const shardNamePattern = "shard-\\d+";
const shardTagPattern = new RegExp(`\\[(${shardNamePattern})\\]`, "g");
const trailingShardTagsPattern = new RegExp(`(\\.\\[${shardNamePattern}\\])+$`);
export const getE2eFileShards = (fileName: string) =>
[...fileName.matchAll(shardTagPattern)].map((match) => match[1]);
export const getE2eSuiteName = (fileName: string) => {
const name = fileName.endsWith(e2eFileSuffix)
? fileName.slice(0, -e2eFileSuffix.length)
: fileName;
return name.replace(trailingShardTagsPattern, "").replaceAll("-", " ");
};
export const getE2eShards = (fileNames: readonly string[]) => {
const e2eFiles = fileNames
.filter((fileName) => fileName.endsWith(e2eFileSuffix))
.map((fileName) => ({ fileName, shards: getE2eFileShards(fileName) }));
const unassignedFiles = e2eFiles
.filter(({ shards }) => shards.length === 0)
.map(({ fileName }) => fileName);
if (unassignedFiles.length > 0) {
throw new Error(
`Every e2e file must have a shard tag: ${unassignedFiles.join(", ")}`
);
}
return [...new Set(e2eFiles.flatMap(({ shards }) => shards))].sort(
(left, right) => left.localeCompare(right, undefined, { numeric: true })
);
};
export const getE2eTestModules = (
fileNames: readonly string[],
shard?: string
) =>
fileNames
.filter((fileName) => fileName.endsWith(e2eFileSuffix))
.filter(
(fileName) =>
shard === undefined ||
shard === "" ||
getE2eFileShards(fileName).includes(shard)
)
.sort()
.map((fileName) => `./tests/${fileName}`);
export const isE2eTestInShard = ({
fileName,
shard,
distributionIndex,
}: {
fileName: string;
shard: string | undefined;
distributionIndex: number;
}) => {
if (shard === undefined || shard === "") {
return true;
}
const shards = getE2eFileShards(fileName);
const shardIndex = shards.indexOf(shard);
return shardIndex !== -1 && distributionIndex % shards.length === shardIndex;
};