Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Playwright --repeat-each #1343

Merged
merged 2 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions apps/backend/db/migrations/20240706121810_screenshot-base-name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @param {import('knex').Knex} knex
*/
export const up = async (knex) => {
await knex.schema.alterTable("screenshots", (table) => {
table.string("baseName", 1024);
});
};

/**
* @param {import('knex').Knex} knex
*/
export const down = async (knex) => {
await knex.schema.alterTable("screenshots", (table) => {
table.dropColumn("baseName");
});
};
6 changes: 4 additions & 2 deletions apps/backend/db/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1019,7 +1019,8 @@ CREATE TABLE public.screenshots (
metadata jsonb,
"playwrightTraceFileId" bigint,
"buildShardId" bigint,
threshold real
threshold real,
"baseName" character varying(1024)
);


Expand Down Expand Up @@ -2807,4 +2808,5 @@ INSERT INTO public.knex_migrations(name, batch, migration_time) VALUES ('2024050
INSERT INTO public.knex_migrations(name, batch, migration_time) VALUES ('20240604133729_comment_id_big_integer.js', 1, NOW());
INSERT INTO public.knex_migrations(name, batch, migration_time) VALUES ('20240614204320_build_shards.js', 1, NOW());
INSERT INTO public.knex_migrations(name, batch, migration_time) VALUES ('20240616142430_build_shards_indices.js', 1, NOW());
INSERT INTO public.knex_migrations(name, batch, migration_time) VALUES ('20240630151704_screenshot-threshold.js', 1, NOW());
INSERT INTO public.knex_migrations(name, batch, migration_time) VALUES ('20240630151704_screenshot-threshold.js', 1, NOW());
INSERT INTO public.knex_migrations(name, batch, migration_time) VALUES ('20240706121810_screenshot-base-name.js', 1, NOW());
9 changes: 6 additions & 3 deletions apps/backend/src/build/createBuildDiffs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,12 @@ export async function createBuildDiffs(build: Build) {
"no base screenshots found for build",
);

return baseScreenshotBucket.screenshots.find(
({ name }) => name === compareScreenshot.name,
);
return baseScreenshotBucket.screenshots.find(({ name }) => {
if (compareScreenshot.baseName) {
return name === compareScreenshot.baseName;
}
return name === compareScreenshot.name;
});
})();

const sameFileId = Boolean(
Expand Down
27 changes: 23 additions & 4 deletions apps/backend/src/database/models/Screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type ScreenshotMetadata = {
};
retry?: number;
retries?: number;
repeat?: number;
} | null;
browser?: {
name: string;
Expand All @@ -51,10 +52,12 @@ export const ScreenshotMetadataJsonSchema = {
type: "object",
properties: {
width: {
type: "number",
type: "integer",
minimum: 0,
},
height: {
type: "number",
type: "integer",
minimum: 0,
},
},
required: ["width", "height"],
Expand Down Expand Up @@ -84,17 +87,31 @@ export const ScreenshotMetadataJsonSchema = {
type: "string",
},
},
retries: {
type: "integer",
minimum: 0,
},
retry: {
type: "integer",
minimum: 0,
},
repeat: {
type: "integer",
minimum: 0,
},
location: {
type: "object",
properties: {
file: {
type: "string",
},
line: {
type: "number",
type: "integer",
minimum: 0,
},
column: {
type: "number",
type: "integer",
minimum: 0,
},
},
required: ["file", "line", "column"],
Expand Down Expand Up @@ -155,6 +172,7 @@ export class Screenshot extends Model {
required: ["name", "s3Id", "screenshotBucketId"],
properties: {
name: { type: "string", maxLength: 1024 },
baseName: { type: ["string", "null"], maxLength: 1024 },
s3Id: { type: "string" },
screenshotBucketId: { type: "string" },
fileId: { type: ["string", "null"] },
Expand All @@ -167,6 +185,7 @@ export class Screenshot extends Model {
});

name!: string;
baseName!: string | null;
s3Id!: string;
screenshotBucketId!: string;
fileId!: string | null;
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/src/database/services/screenshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type InsertFilesAndScreenshotsParams = {
metadata?: ScreenshotMetadata | null;
pwTraceKey?: string | null;
threshold?: number | null;
baseName?: string | null;
}[];
build: Build;
shard?: BuildShard | null;
Expand Down Expand Up @@ -170,6 +171,7 @@ export async function insertFilesAndScreenshots(
playwrightTraceFileId: pwTraceFile?.id ?? null,
buildShardId: params.shard?.id ?? null,
threshold: screenshot.threshold ?? null,
baseName: screenshot.baseName ?? null,
};
}),
);
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/src/graphql/__generated__/resolver-types.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/backend/src/graphql/__generated__/schema.gql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/backend/src/graphql/definitions/Screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const typeDefs = gql`
location: ScreenshotMetadataLocation
retry: Int
retries: Int
repeat: Int
}

type ScreenshotMetadataBrowser {
Expand Down
5 changes: 5 additions & 0 deletions apps/backend/src/web/api/v2/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ export const validateUpdateRequest = validate({
name: {
type: "string",
},
baseName: {
type: "string",
nullable: true,
},
metadata: ScreenshotMetadataJsonSchema,
pwTraceKey: {
type: "string",
Expand Down Expand Up @@ -227,6 +231,7 @@ export type UpdateRequest = Request<
screenshots: {
key: string;
name: string;
baseName?: string | null;
metadata?: ScreenshotMetadata | null;
pwTraceKey?: string | null;
threshold?: number | null;
Expand Down
13 changes: 7 additions & 6 deletions apps/backend/src/web/app.api-v2.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ describe("api v2", () => {
{
key: "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
name: "first",
baseName: "first-base",
threshold: 0.3,
path: join(__dirname, "__fixtures__", "screenshot_test.jpg"),
},
{
Expand Down Expand Up @@ -206,7 +208,8 @@ describe("api v2", () => {
key: screenshot.key,
name: screenshot.name,
metadata: screenshot.metadata,
threshold: 0.3,
threshold: screenshot.threshold,
baseName: screenshot.baseName ?? null,
})),
})
.expect(200);
Expand All @@ -216,11 +219,9 @@ describe("api v2", () => {
.first()
.throwIfNotFound();

expect(
build.compareScreenshotBucket!.screenshots?.every(
(s) => s.threshold === 0.3,
),
).toBe(true);
const firstScreenshot = build.compareScreenshotBucket!.screenshots![0]!;
expect(firstScreenshot.threshold).toBe(0.3);
expect(firstScreenshot.baseName).toBe("first-base");

const screenshotWithMetadata =
build.compareScreenshotBucket!.screenshots!.find(
Expand Down
4 changes: 2 additions & 2 deletions apps/frontend/src/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const documents = {
"\n query AccountSettings_account($slug: String!) {\n account(slug: $slug) {\n id\n\n ... on Team {\n plan {\n id\n fineGrainedAccessControlIncluded\n }\n }\n\n ...TeamSlack_Account\n ...TeamMembers_Team\n ...TeamDelete_Team\n ...AccountChangeName_Account\n ...AccountChangeSlug_Account\n ...PlanCard_Account\n ...AccountGitLab_Account\n ...TeamGitHubSSO_Team\n ...TeamAccessRole_Team\n }\n }\n": types.AccountSettings_AccountDocument,
"\n query Account_account($slug: String!) {\n account(slug: $slug) {\n id\n slug\n permissions\n ...PaymentBanner_Account\n }\n }\n": types.Account_AccountDocument,
"\n fragment BuildDetail_Build on Build {\n id\n stats {\n total\n }\n createdAt\n branch\n type\n baseScreenshotBucket {\n id\n branch\n createdAt\n }\n pullRequest {\n merged\n }\n }\n": types.BuildDetail_BuildFragmentDoc,
"\n fragment BuildDiffState_ScreenshotDiff on ScreenshotDiff {\n id\n status\n url\n name\n width\n height\n group\n threshold\n baseScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n }\n }\n }\n compareScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n }\n }\n playwrightTraceUrl\n }\n }\n": types.BuildDiffState_ScreenshotDiffFragmentDoc,
"\n fragment BuildDiffState_ScreenshotDiff on ScreenshotDiff {\n id\n status\n url\n name\n width\n height\n group\n threshold\n baseScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n repeat\n }\n }\n }\n compareScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n repeat\n }\n }\n playwrightTraceUrl\n }\n }\n": types.BuildDiffState_ScreenshotDiffFragmentDoc,
"\n query BuildDiffState_Project(\n $accountSlug: String!\n $projectName: String!\n $buildNumber: Int!\n $after: Int!\n $first: Int!\n ) {\n project(accountSlug: $accountSlug, projectName: $projectName) {\n id\n build(number: $buildNumber) {\n id\n screenshotDiffs(after: $after, first: $first) {\n pageInfo {\n hasNextPage\n }\n edges {\n ...BuildDiffState_ScreenshotDiff\n }\n }\n }\n }\n }\n": types.BuildDiffState_ProjectDocument,
"\n fragment BuildDiffState_Build on Build {\n id\n stats {\n ...BuildStatsIndicator_BuildStats\n total\n failure\n changed\n added\n removed\n unchanged\n retryFailure\n }\n }\n": types.BuildDiffState_BuildFragmentDoc,
"\n fragment BuildInfos_Build on Build {\n createdAt\n name\n commit\n branch\n mode\n stats {\n total\n }\n baseScreenshotBucket {\n id\n commit\n branch\n }\n baseBuild {\n id\n number\n }\n pullRequest {\n id\n url\n number\n }\n }\n": types.BuildInfos_BuildFragmentDoc,
Expand Down Expand Up @@ -515,7 +515,7 @@ export function graphql(source: "\n fragment BuildDetail_Build on Build {\n
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment BuildDiffState_ScreenshotDiff on ScreenshotDiff {\n id\n status\n url\n name\n width\n height\n group\n threshold\n baseScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n }\n }\n }\n compareScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n }\n }\n playwrightTraceUrl\n }\n }\n"): (typeof documents)["\n fragment BuildDiffState_ScreenshotDiff on ScreenshotDiff {\n id\n status\n url\n name\n width\n height\n group\n threshold\n baseScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n }\n }\n }\n compareScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n }\n }\n playwrightTraceUrl\n }\n }\n"];
export function graphql(source: "\n fragment BuildDiffState_ScreenshotDiff on ScreenshotDiff {\n id\n status\n url\n name\n width\n height\n group\n threshold\n baseScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n repeat\n }\n }\n }\n compareScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n repeat\n }\n }\n playwrightTraceUrl\n }\n }\n"): (typeof documents)["\n fragment BuildDiffState_ScreenshotDiff on ScreenshotDiff {\n id\n status\n url\n name\n width\n height\n group\n threshold\n baseScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n repeat\n }\n }\n }\n compareScreenshot {\n id\n url\n width\n height\n metadata {\n url\n colorScheme\n mediaType\n automationLibrary {\n name\n version\n }\n browser {\n name\n version\n }\n sdk {\n name\n version\n }\n viewport {\n width\n height\n }\n test {\n id\n title\n titlePath\n location {\n file\n line\n }\n retry\n retries\n repeat\n }\n }\n playwrightTraceUrl\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
Loading