Skip to content

Commit cb6a11f

Browse files
authored
Enabled auto approve passes all tests #266 (#133)
closes Visual-Regression-Tracker/Visual-Regression-Tracker#266
1 parent 8c29f1c commit cb6a11f

File tree

5 files changed

+210
-30
lines changed

5 files changed

+210
-30
lines changed

src/_data_/index.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Build, ImageComparison, Project } from '@prisma/client';
1+
import { Baseline, Build, ImageComparison, Project, TestRun, TestVariation } from '@prisma/client';
22

33
export const TEST_PROJECT: Project = {
44
id: '1',
@@ -26,3 +26,67 @@ export const TEST_BUILD: Build = {
2626
userId: '2341235',
2727
isRunning: true,
2828
};
29+
30+
export const generateBaseline = (baseline?: Partial<Baseline>): Baseline => {
31+
return {
32+
id: 'baselineId',
33+
baselineName: 'baselineName',
34+
testVariationId: 'testVariationId',
35+
testRunId: 'testRunId',
36+
updatedAt: new Date(),
37+
createdAt: new Date(),
38+
...baseline,
39+
};
40+
};
41+
42+
export const generateTestVariation = (
43+
testVariation?: Partial<TestVariation>,
44+
baselines?: Baseline[]
45+
): TestVariation & { baselines: Baseline[] } => {
46+
return {
47+
id: '123',
48+
projectId: 'project Id',
49+
name: 'Test name',
50+
baselineName: 'baselineName',
51+
os: 'OS',
52+
browser: 'browser',
53+
viewport: 'viewport',
54+
device: 'device',
55+
ignoreAreas: '[]',
56+
comment: 'some comment',
57+
createdAt: new Date(),
58+
updatedAt: new Date(),
59+
branchName: 'master',
60+
...testVariation,
61+
baselines: baselines ?? [generateBaseline()],
62+
};
63+
};
64+
65+
export const generateTestRun = (testRun?: Partial<TestRun>): TestRun => {
66+
return {
67+
id: '10fb5e02-64e0-4cf5-9f17-c00ab3c96658',
68+
imageName: '1592423768112.screenshot.png',
69+
diffName: 'diffName',
70+
diffPercent: 12,
71+
diffTollerancePercent: 1,
72+
pixelMisMatchCount: 123,
73+
status: 'new',
74+
buildId: '146e7a8d-89f0-4565-aa2c-e61efabb0afd',
75+
testVariationId: '3bc4a5bc-006e-4d43-8e4e-eaa132627fca',
76+
updatedAt: new Date(),
77+
createdAt: new Date(),
78+
name: 'ss2f77',
79+
browser: 'chromium',
80+
device: null,
81+
os: null,
82+
viewport: '1800x1600',
83+
baselineName: null,
84+
ignoreAreas: '[]',
85+
tempIgnoreAreas: '[]',
86+
comment: 'some comment',
87+
baselineBranchName: 'master',
88+
branchName: 'develop',
89+
merge: false,
90+
...testRun,
91+
};
92+
};

src/test-runs/test-runs.service.spec.ts

Lines changed: 125 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { CommentDto } from '../shared/dto/comment.dto';
1212
import { TestVariationsService } from '../test-variations/test-variations.service';
1313
import { TestRunDto } from '../test-runs/dto/testRun.dto';
1414
import { BuildsService } from '../builds/builds.service';
15-
import { TEST_PROJECT } from '../_data_';
15+
import { generateBaseline, generateTestRun, generateTestVariation, TEST_PROJECT } from '../_data_';
1616
import { getTestVariationUniqueData } from '../utils';
1717
import { BaselineDataDto } from '../shared/dto/baseline-data.dto';
1818
import { CompareService } from '../compare/compare.service';
@@ -39,6 +39,7 @@ const initService = async ({
3939
testVariationFindManyMock = jest.fn(),
4040
baselineCreateMock = jest.fn(),
4141
testVariationFindOrCreateMock = jest.fn(),
42+
testVariationGetDetailsMock = jest.fn(),
4243
testVariationFindUniqueMock = jest.fn(),
4344
projectFindUniqueMock = jest.fn(),
4445
compareGetDiffMock = jest.fn(),
@@ -92,6 +93,7 @@ const initService = async ({
9293
provide: TestVariationsService,
9394
useValue: {
9495
findOrCreate: testVariationFindOrCreateMock,
96+
getDetails: testVariationGetDetailsMock,
9597
},
9698
},
9799
{
@@ -114,31 +116,6 @@ describe('TestRunsService', () => {
114116
const imageBuffer = Buffer.from('Image');
115117
const ignoreAreas = [{ x: 1, y: 2, width: 10, height: 20 }];
116118
const tempIgnoreAreas = [{ x: 3, y: 4, width: 30, height: 40 }];
117-
const baseTestRun: TestRun = {
118-
id: 'id',
119-
imageName: 'imageName',
120-
diffName: 'diffName',
121-
baselineName: 'baselineName',
122-
diffPercent: 1,
123-
pixelMisMatchCount: 10,
124-
diffTollerancePercent: 12,
125-
status: TestStatus.new,
126-
buildId: 'buildId',
127-
testVariationId: 'testVariationId',
128-
updatedAt: new Date(),
129-
createdAt: new Date(),
130-
name: 'test run name',
131-
ignoreAreas: '[]',
132-
tempIgnoreAreas: '[]',
133-
browser: 'browser',
134-
device: 'device',
135-
os: 'os',
136-
viewport: 'viewport',
137-
branchName: 'develop',
138-
baselineBranchName: 'master',
139-
comment: 'some comment',
140-
merge: false,
141-
};
142119

143120
it('findOne', async () => {
144121
const id = 'some id';
@@ -657,4 +634,126 @@ describe('TestRunsService', () => {
657634
expect(service['tryAutoApproveByNewBaselines']).toHaveBeenCalledWith({ testVariation, testRun });
658635
expect(mocked(TestRunResultDto)).toHaveBeenCalledWith(testRun, testVariation);
659636
});
637+
638+
describe('tryAutoApproveByNewBaselines', () => {
639+
it('skip if ok', async () => {
640+
const testRun = generateTestRun({ status: TestStatus.ok });
641+
service = await initService({});
642+
643+
const result = await service['tryAutoApproveByNewBaselines']({
644+
testVariation: generateTestVariation(),
645+
testRun,
646+
});
647+
648+
expect(result).toBe(testRun);
649+
});
650+
651+
it('should not auto approve', async () => {
652+
const testRun = generateTestRun({ status: TestStatus.unresolved });
653+
const baseline = generateBaseline();
654+
const testRunFindManyMock = jest.fn().mockResolvedValueOnce([generateTestRun({ status: TestStatus.approved })]);
655+
const testVariationGetDetailsMock = jest.fn().mockResolvedValueOnce(generateTestVariation({}, [baseline]));
656+
service = await initService({ testRunFindManyMock, testVariationGetDetailsMock });
657+
service['shouldAutoApprove'] = jest.fn().mockResolvedValueOnce(false);
658+
659+
const result = await service['tryAutoApproveByNewBaselines']({
660+
testVariation: generateTestVariation(),
661+
testRun,
662+
});
663+
664+
expect(service['shouldAutoApprove']).toHaveBeenCalled();
665+
expect(result).toBe(testRun);
666+
});
667+
668+
it('should auto approve', async () => {
669+
const testRun = generateTestRun({ status: TestStatus.unresolved });
670+
const baseline = generateBaseline();
671+
const testRunFindManyMock = jest.fn().mockResolvedValueOnce([generateTestRun({ status: TestStatus.approved })]);
672+
const testVariationGetDetailsMock = jest.fn().mockResolvedValueOnce(generateTestVariation({}, [baseline]));
673+
service = await initService({ testRunFindManyMock, testVariationGetDetailsMock });
674+
service['shouldAutoApprove'] = jest.fn().mockResolvedValueOnce(true);
675+
service.approve = jest.fn().mockResolvedValueOnce({
676+
...testRun,
677+
status: TestStatus.autoApproved,
678+
});
679+
680+
const result = await service['tryAutoApproveByNewBaselines']({
681+
testVariation: generateTestVariation(),
682+
testRun,
683+
});
684+
685+
expect(result).toStrictEqual({
686+
...testRun,
687+
status: TestStatus.autoApproved,
688+
});
689+
});
690+
});
691+
692+
describe('tryAutoApproveByPastBaselines', () => {
693+
it('skip if ok', async () => {
694+
const testRun = generateTestRun({ status: TestStatus.ok });
695+
service = await initService({});
696+
697+
const result = await service['tryAutoApproveByPastBaselines']({
698+
testVariation: generateTestVariation(),
699+
testRun,
700+
});
701+
702+
expect(result).toBe(testRun);
703+
});
704+
705+
it('skip if the branch the same as baseline', async () => {
706+
const testRun = generateTestRun({ status: TestStatus.unresolved, branchName: 'a', baselineBranchName: 'a' });
707+
service = await initService({});
708+
709+
const result = await service['tryAutoApproveByPastBaselines']({
710+
testVariation: generateTestVariation(),
711+
testRun,
712+
});
713+
714+
expect(result).toBe(testRun);
715+
});
716+
717+
it('should not auto approve', async () => {
718+
const testRun = generateTestRun({ status: TestStatus.unresolved });
719+
const baseline = generateBaseline();
720+
const testVariationGetDetailsMock = jest
721+
.fn()
722+
.mockResolvedValueOnce(generateTestVariation({}, [baseline, baseline]));
723+
service = await initService({ testVariationGetDetailsMock });
724+
service['shouldAutoApprove'] = jest.fn().mockResolvedValueOnce(false);
725+
726+
const result = await service['tryAutoApproveByPastBaselines']({
727+
testVariation: generateTestVariation(),
728+
testRun,
729+
});
730+
731+
expect(service['shouldAutoApprove']).toHaveBeenCalled();
732+
expect(result).toBe(testRun);
733+
});
734+
735+
it('should auto approve', async () => {
736+
const testRun = generateTestRun({ status: TestStatus.unresolved });
737+
const baseline = generateBaseline();
738+
const testVariationGetDetailsMock = jest
739+
.fn()
740+
.mockResolvedValueOnce(generateTestVariation({}, [baseline, baseline]));
741+
service = await initService({ testVariationGetDetailsMock });
742+
service['shouldAutoApprove'] = jest.fn().mockResolvedValueOnce(true);
743+
service.approve = jest.fn().mockResolvedValueOnce({
744+
...testRun,
745+
status: TestStatus.autoApproved,
746+
});
747+
748+
const result = await service['tryAutoApproveByPastBaselines']({
749+
testVariation: generateTestVariation(),
750+
testRun,
751+
});
752+
753+
expect(result).toStrictEqual({
754+
...testRun,
755+
status: TestStatus.autoApproved,
756+
});
757+
});
758+
});
660759
});

src/test-runs/test-runs.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ export class TestRunsService {
284284
const testVariationHistory = await this.testVariationService.getDetails(testVariation.id);
285285
// skip first baseline as it was used by default in general flow
286286
for (const baseline of testVariationHistory.baselines.slice(1)) {
287-
if (this.shouldAutoApprove({ projectId: testVariation.projectId, baseline, testRun })) {
287+
if (await this.shouldAutoApprove({ projectId: testVariation.projectId, baseline, testRun })) {
288288
return this.approve(testRun.id, false, true);
289289
}
290290
}
@@ -321,7 +321,7 @@ export class TestRunsService {
321321
const approvedTestVariation = await this.testVariationService.getDetails(approvedTestRun.testVariationId);
322322
const baseline = approvedTestVariation.baselines.shift();
323323

324-
if (this.shouldAutoApprove({ projectId: testVariation.projectId, baseline, testRun })) {
324+
if (await this.shouldAutoApprove({ projectId: testVariation.projectId, baseline, testRun })) {
325325
return this.approve(testRun.id, false, true);
326326
}
327327
}

test/image_edited_2.png

97.8 KB
Loading

test/test-runs.e2e-spec.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ describe('TestRuns (e2e)', () => {
2525

2626
const image_v1 = './test/image.png';
2727
const image_v2 = './test/image_edited.png';
28+
const image_v3 = './test/image_edited_2.png';
2829

2930
beforeAll(async () => {
3031
const moduleFixture: TestingModule = await Test.createTestingModule({
@@ -75,8 +76,24 @@ describe('TestRuns (e2e)', () => {
7576
image_v1
7677
);
7778
await testRunsService.approve(testRun1.id);
79+
const { testRun: testRun2 } = await haveTestRunCreated(
80+
buildsService,
81+
testRunsService,
82+
project.id,
83+
project.mainBranchName,
84+
image_v2
85+
);
86+
await testRunsService.approve(testRun2.id);
87+
const { testRun: testRun3 } = await haveTestRunCreated(
88+
buildsService,
89+
testRunsService,
90+
project.id,
91+
"feature",
92+
image_v2
93+
);
94+
await testRunsService.approve(testRun3.id);
7895

79-
const { testRun } = await haveTestRunCreated(buildsService, testRunsService, project.id, 'develop', image_v2);
96+
const { testRun } = await haveTestRunCreated(buildsService, testRunsService, project.id, 'develop', image_v3);
8097

8198
expect(testRun.status).toBe(TestStatus.unresolved);
8299
});

0 commit comments

Comments
 (0)