Skip to content

Commit e24ccb1

Browse files
authored
Fix download images (#344)
* Fix download images related to Visual-Regression-Tracker/Visual-Regression-Tracker#417
1 parent 9809c53 commit e24ccb1

File tree

12 files changed

+147
-109
lines changed

12 files changed

+147
-109
lines changed

README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@
1111

1212
The testing related `.spec.tsx` files are used with Playwright for browser tests, and the `.test.tsx` files with Jest for unit tests.
1313

14-
## Image download
15-
16-
- If you want to use image download feature in test runs, you have to have the files in backend imageUploads folder to a folder in this project under public/static/imageUploads. This can be achieved via manual copy, docker volume mapping to this project folder etc.
17-
1814
## Local HTTPS config
1915

2016
- Generate keys [here](https://www.selfsignedcertificate.com/)

integration_tests/fixtures/index.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@ type Fixtures = {
1010
vrt: PlaywrightVisualRegressionTracker;
1111
loginPage: LoginPage;
1212
registerPage: RegisterPage;
13-
openProjectPage: (id: string) => Promise<ProjectPage>;
13+
openProjectPage: (
14+
id: string,
15+
buildId?: string,
16+
testId?: string
17+
) => Promise<ProjectPage>;
1418
openTestVariationListPage: (
15-
projectId: string,
19+
projectId: string
1620
) => Promise<TestVariationListPage>;
1721
openTestVariationDetailsPage: (
18-
id: string,
22+
id: string
1923
) => Promise<TestVariationDetailsPage>;
2024
projectListPage: ProjectListPage;
2125
profilePage: ProfilePage;
@@ -64,8 +68,15 @@ export const test = base.extend<Fixtures>({
6468
},
6569
// eslint-disable-next-line @typescript-eslint/no-unused-vars
6670
openProjectPage: async ({ page, authUser }, use) => {
67-
await use(async (id) => {
68-
await page.goto(`${id}`);
71+
await use(async (id, buildId, testId) => {
72+
let url = `${id}`;
73+
if (buildId) {
74+
url = url.concat(`?buildId=${buildId}`);
75+
if (testId) {
76+
url = url.concat(`&testId=${testId}`);
77+
}
78+
}
79+
await page.goto(url);
6980
return new ProjectPage(page);
7081
});
7182
},
@@ -95,8 +106,8 @@ export const test = base.extend<Fixtures>({
95106
apiKey: "ASJDHGAKJSDGASD",
96107
role: "admin",
97108
token: "eyJsgOE8Bw2bFwhZAugRRGm8U",
98-
}),
99-
),
109+
})
110+
)
100111
);
101112

102113
await use();
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
import { Page } from "@playwright/test";
12
import { BasePage } from "./BasePage";
3+
import { TestRunList } from "./components/TestRunList";
4+
import { BuildList } from "./components/BuildList";
25

36
export class ProjectPage extends BasePage {
4-
buildList = this.page.locator("#build-list");
5-
testRunList = this.page.locator("#test-run-list");
7+
testRunList: TestRunList;
8+
buildList: BuildList;
69

7-
getBuildLocator(number: number) {
8-
return this.buildList.getByText(`#${number}`);
9-
}
10-
11-
getTestRunLocator(name: string) {
12-
return this.testRunList.getByText(name);
10+
constructor(page: Page) {
11+
super(page);
12+
this.testRunList = new TestRunList(page);
13+
this.buildList = new BuildList(page);
1314
}
1415
}

integration_tests/pages/UserListPage.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
import { Locator } from "@playwright/test";
1+
import { Page } from "@playwright/test";
22
import { BasePage } from "./BasePage";
33
import { type Role } from "~client/types";
4+
import { Table } from "./components/Table";
45

56
export class UserListPage extends BasePage {
6-
private getRow(userId: string): Locator {
7-
return this.page.locator(`[data-id='${userId}']`);
7+
table: Table;
8+
9+
constructor(page: Page) {
10+
super(page);
11+
this.table = new Table(page);
812
}
913

1014
async changeRole(userId: string, role: keyof typeof Role) {
11-
await this.getRow(userId).locator("[data-field='role']").dblclick();
15+
await this.table.getColumn(userId, "role").dblclick();
1216
await this.page.locator(`[data-value='${role}']`).click();
1317

1418
return this.page.keyboard.press("Enter");
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Page } from "@playwright/test";
2+
3+
export class BuildList {
4+
buildList = this.page.locator("#build-list");
5+
6+
constructor(public page: Page) {
7+
this.page = page;
8+
}
9+
10+
getBuildLocator(number: number) {
11+
return this.buildList.getByText(`#${number}`);
12+
}
13+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Locator, Page } from "@playwright/test";
2+
3+
export class Table {
4+
constructor(public page: Page) {
5+
this.page = page;
6+
}
7+
8+
getRow(rowId: string): Locator {
9+
return this.page.locator(`[data-id='${rowId}']`);
10+
}
11+
12+
getColumn(rowId: string, name: string) {
13+
return this.getRow(rowId).locator(`[data-field='${name}']`);
14+
}
15+
16+
async checkRow(id: string) {
17+
await this.getColumn(id, "__check__").click();
18+
}
19+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Table } from "./Table";
2+
3+
export class TestRunList extends Table {
4+
downloadBtn = this.page.getByTestId("CloudDownloadIcon");
5+
}

integration_tests/test/projec.spec.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { expect } from "@playwright/test";
12
import { test } from "fixtures";
23
import {
34
TEST_BUILD_FAILED,
@@ -42,12 +43,25 @@ test.beforeEach(async ({ page }) => {
4243

4344
test("renders", async ({ openProjectPage, page, vrt }) => {
4445
const projectPage = await openProjectPage(project.id);
45-
await projectPage.getBuildLocator(TEST_BUILD_FAILED.number).click();
46-
await page.waitForTimeout(500); //TODO: fixt flacky screen
46+
await projectPage.buildList.getBuildLocator(TEST_BUILD_FAILED.number).click();
4747

4848
await vrt.trackPage(page, "Project page. Test run list");
4949

50-
await projectPage.getTestRunLocator(TEST_UNRESOLVED.name).click();
50+
await projectPage.testRunList.getRow(TEST_UNRESOLVED.id).click();
5151

5252
await vrt.trackPage(page, "Project page. Test run details");
5353
});
54+
55+
test("can download images", async ({ openProjectPage, page }) => {
56+
const projectPage = await openProjectPage(project.id, TEST_BUILD_FAILED.id);
57+
58+
await projectPage.testRunList.checkRow(TEST_UNRESOLVED.id);
59+
await projectPage.testRunList.checkRow(TEST_RUN_NEW.id);
60+
61+
await projectPage.testRunList.downloadBtn.click();
62+
await projectPage.modal.confirmBtn.click();
63+
64+
await expect(projectPage.notification.message).toHaveText(
65+
"2 test runs processed."
66+
);
67+
});

src/_test/test.data.helper.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ export const TEST_UNRESOLVED: TestRun = {
223223
export const TEST_RUN_APPROVED: TestRun = {
224224
id: "some_test_run_id2",
225225
buildId: "some build id",
226-
imageName: "imageName",
227-
diffName: "diffName",
226+
imageName: "image.png",
227+
diffName: "diff.png",
228228
diffPercent: 1.24,
229229
diffTollerancePercent: 3.21,
230230
status: TestStatus.approved,
@@ -247,8 +247,8 @@ export const TEST_RUN_APPROVED: TestRun = {
247247
export const TEST_RUN_NEW: TestRun = {
248248
id: "some_test_run_id3",
249249
buildId: "some build id",
250-
imageName: "imageName",
251-
diffName: "diffName",
250+
imageName: "image.png",
251+
diffName: "diff.png",
252252
diffPercent: 1.24,
253253
diffTollerancePercent: 3.21,
254254
status: TestStatus.new,
@@ -271,8 +271,8 @@ export const TEST_RUN_NEW: TestRun = {
271271
export const TEST_RUN_OK: TestRun = {
272272
id: "some_test_run_id4",
273273
buildId: "some build id",
274-
imageName: "imageName",
275-
diffName: "diffName",
274+
imageName: "image.png",
275+
diffName: "diff.png",
276276
diffPercent: 1.24,
277277
diffTollerancePercent: 3.21,
278278
status: TestStatus.ok,

src/components/TestRunList/BulkOperation.tsx

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,21 @@ import {
1616
ThumbDown,
1717
ThumbUp,
1818
} from "@mui/icons-material";
19-
import { testRunService } from "../../services";
19+
import { staticService, testRunService } from "../../services";
2020
import { TestStatus } from "../../types";
2121
import { Tooltip } from "../Tooltip";
22+
import { useTestRunState } from "../../contexts";
2223

2324
export const BulkOperation: React.FunctionComponent = () => {
2425
const apiRef = useGridApiContext();
2526
const { state } = apiRef.current;
2627
const rows = gridExpandedSortedRowEntriesSelector(
2728
state,
28-
apiRef.current.instanceId,
29+
apiRef.current.instanceId
2930
);
3031

3132
const selectedRows = gridRowSelectionStateSelector(state);
32-
33+
const { testRuns } = useTestRunState();
3334
const { enqueueSnackbar } = useSnackbar();
3435
const [approveDialogOpen, setApproveDialogOpen] = React.useState(false);
3536
const [rejectDialogOpen, setRejectDialogOpen] = React.useState(false);
@@ -42,15 +43,15 @@ export const BulkOperation: React.FunctionComponent = () => {
4243
const [isProcessing, setIsProcessing] = React.useState(false);
4344
const selectedIds: GridRowId[] = React.useMemo(
4445
() => Object.values(selectedRows),
45-
[selectedRows],
46+
[selectedRows]
4647
);
4748

4849
const isMerge: boolean = React.useMemo(
4950
() =>
5051
!!rows.find((row: GridRowModel) =>
51-
selectedIds.includes(row.id.toString()),
52+
selectedIds.includes(row.id.toString())
5253
),
53-
[selectedIds, rows],
54+
[selectedIds, rows]
5455
);
5556

5657
// The ids of those rows that have status "new" or "resolved"
@@ -61,11 +62,11 @@ export const BulkOperation: React.FunctionComponent = () => {
6162
(row: GridRowModel) =>
6263
selectedIds.includes(row.id.toString()) &&
6364
[TestStatus.new, TestStatus.unresolved].includes(
64-
row.model.status.toString(),
65-
),
65+
row.model.status.toString()
66+
)
6667
)
6768
.map((row: GridRowModel) => row.id.toString()),
68-
[selectedIds, rows],
69+
[selectedIds, rows]
6970
);
7071

7172
const toggleApproveDialogOpen = () => {
@@ -145,29 +146,19 @@ export const BulkOperation: React.FunctionComponent = () => {
145146
const getBulkAction = () => {
146147
if (deleteDialogOpen) {
147148
return testRunService.removeBulk(
148-
selectedIds.map((item: GridRowId) => item.toString()),
149+
selectedIds.map((item: GridRowId) => item.toString())
149150
);
150151
}
151152

152153
if (downloadDialogOpen) {
153-
const urlsToDownload: {
154-
download: string;
155-
filename: string;
156-
}[] = [];
157-
158-
for (const id of selectedIds) {
159-
testRunService.getDetails(id.toString()).then((testRun) => {
160-
urlsToDownload.push({
161-
download: "static/imageUploads/" + testRun.imageName,
162-
filename: testRun.name,
163-
});
164-
165-
//Call getFile function only when all images names are pushed into the array.
166-
if (urlsToDownload.length === selectedIds.length) {
167-
testRunService.getFiles(urlsToDownload);
168-
}
169-
});
170-
}
154+
const images = testRuns
155+
.filter((testRun) => selectedIds.includes(testRun.id))
156+
.map((item) => ({
157+
filename: item.name,
158+
url: staticService.getImage(item.imageName),
159+
}));
160+
161+
return staticService.downloadAsZip(images);
171162
}
172163

173164
if (rejectDialogOpen) {

0 commit comments

Comments
 (0)