From debd2201dd2b8b946e02a662cddfada00ef38446 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 10 Oct 2020 22:12:12 +0300 Subject: [PATCH 1/5] TestRunResult added https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker/issues/130 --- lib/testRunResult.spec.ts | 46 +++++++++++++++ lib/testRunResult.ts | 19 +++++++ lib/types/index.ts | 4 +- lib/types/response/testRunResponse.ts | 14 +++++ lib/types/testRunResult.ts | 9 --- lib/types/{testRunStatus.ts => testStatus.ts} | 2 +- lib/visualRegressionTracker.spec.ts | 57 +++++++++++++------ lib/visualRegressionTracker.ts | 27 ++++++--- 8 files changed, 139 insertions(+), 39 deletions(-) create mode 100644 lib/testRunResult.spec.ts create mode 100644 lib/testRunResult.ts create mode 100644 lib/types/response/testRunResponse.ts delete mode 100644 lib/types/testRunResult.ts rename lib/types/{testRunStatus.ts => testStatus.ts} (67%) diff --git a/lib/testRunResult.spec.ts b/lib/testRunResult.spec.ts new file mode 100644 index 0000000..80498fa --- /dev/null +++ b/lib/testRunResult.spec.ts @@ -0,0 +1,46 @@ +import TestRunResult from "./testRunResult"; +import { TestRunResponse, TestStatus } from "./types"; + +describe("TestRunResult", () => { + it("only required images", () => { + const testRunResponse: TestRunResponse = { + url: "url", + status: TestStatus.ok, + pixelMisMatchCount: 12, + diffPercent: 0.12, + diffTollerancePercent: 0, + id: "some id", + imageName: "imageName", + merge: false, + }; + + const result = new TestRunResult(testRunResponse, "http://localhost"); + + expect(result.testRunResponse).toBe(testRunResponse); + expect(result.imageUrl).toBe("http://localhost/imageName"); + expect(result.diffUrl).toBe(undefined); + expect(result.baselineUrl).toBe(undefined); + }); + + it("all image", () => { + const testRunResponse: TestRunResponse = { + url: "url", + status: TestStatus.ok, + pixelMisMatchCount: 12, + diffPercent: 0.12, + diffTollerancePercent: 0, + id: "some id", + imageName: "imageName", + diffName: "diffName", + baselineName: "baselineName", + merge: false, + }; + + const result = new TestRunResult(testRunResponse, "http://localhost"); + + expect(result.testRunResponse).toBe(testRunResponse); + expect(result.imageUrl).toBe("http://localhost/imageName"); + expect(result.diffUrl).toBe("http://localhost/diffName"); + expect(result.baselineUrl).toBe("http://localhost/baselineName"); + }); +}); diff --git a/lib/testRunResult.ts b/lib/testRunResult.ts new file mode 100644 index 0000000..df4fe1c --- /dev/null +++ b/lib/testRunResult.ts @@ -0,0 +1,19 @@ +import { TestRunResponse } from "./types"; + +export default class TestRunResult { + testRunResponse: TestRunResponse; + imageUrl: string; + diffUrl?: string; + baselineUrl?: string; + + constructor(testRunResponse: TestRunResponse, apiUrl: string) { + this.testRunResponse = testRunResponse; + this.imageUrl = apiUrl.concat("/").concat(testRunResponse.imageName); + this.diffUrl = + testRunResponse.diffName && + apiUrl.concat("/").concat(testRunResponse.diffName); + this.baselineUrl = + testRunResponse.baselineName && + apiUrl.concat("/").concat(testRunResponse.baselineName); + } +} diff --git a/lib/types/index.ts b/lib/types/index.ts index 023462d..538fc5c 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -1,5 +1,5 @@ export * from "./build"; export * from "./config"; export * from "./testRun"; -export * from "./testRunResult"; -export * from "./testRunStatus"; +export * from "./response/testRunResponse"; +export * from "./testStatus"; diff --git a/lib/types/response/testRunResponse.ts b/lib/types/response/testRunResponse.ts new file mode 100644 index 0000000..8af6b61 --- /dev/null +++ b/lib/types/response/testRunResponse.ts @@ -0,0 +1,14 @@ +import { TestStatus } from "../testStatus"; + +export interface TestRunResponse { + id: string; + imageName: string; + diffName?: string; + baselineName?: string; + diffPercent: number; + diffTollerancePercent?: number; + pixelMisMatchCount?: number; + status: TestStatus; + url: string; + merge: boolean; +} diff --git a/lib/types/testRunResult.ts b/lib/types/testRunResult.ts deleted file mode 100644 index fcf9272..0000000 --- a/lib/types/testRunResult.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { TestRunStatus } from "./"; - -export interface TestRunResult { - url: string; - status: TestRunStatus; - pixelMisMatchCount: number; - diffPercent: number; - diffTollerancePercent: number; -} diff --git a/lib/types/testRunStatus.ts b/lib/types/testStatus.ts similarity index 67% rename from lib/types/testRunStatus.ts rename to lib/types/testStatus.ts index 1ff7d81..f1b610d 100644 --- a/lib/types/testRunStatus.ts +++ b/lib/types/testStatus.ts @@ -1,4 +1,4 @@ -export enum TestRunStatus { +export enum TestStatus { new = "new", ok = "ok", unresolved = "unresolved", diff --git a/lib/visualRegressionTracker.spec.ts b/lib/visualRegressionTracker.spec.ts index 7b14c55..c00f1f7 100644 --- a/lib/visualRegressionTracker.spec.ts +++ b/lib/visualRegressionTracker.spec.ts @@ -1,11 +1,15 @@ import { VisualRegressionTracker } from "./visualRegressionTracker"; -import { Config, Build, TestRun, TestRunResult, TestRunStatus } from "./types"; +import { Config, Build, TestRun, TestRunResponse, TestStatus } from "./types"; import { mocked } from "ts-jest/utils"; +import TestRunResult from "./testRunResult"; import axios, { AxiosError, AxiosResponse } from "axios"; jest.mock("axios"); const mockedAxios = mocked(axios, true); +jest.mock("./testRunResult"); +const mockedTestRunResult = mocked(TestRunResult, true); + const axiosError404: AxiosError = { isAxiosError: true, config: {}, @@ -118,44 +122,58 @@ describe("VisualRegressionTracker", () => { }; it("should track success", async () => { - const testRunResult: TestRunResult = { + const testRunResponse: TestRunResponse = { url: "url", - status: TestRunStatus.ok, + status: TestStatus.ok, pixelMisMatchCount: 12, diffPercent: 0.12, diffTollerancePercent: 0, + id: "some id", + imageName: "imageName", + diffName: "diffName", + baselineName: "baselineName", + merge: false, }; - vrt["submitTestResult"] = jest.fn().mockResolvedValueOnce(testRunResult); + vrt["submitTestResult"] = jest + .fn() + .mockResolvedValueOnce(testRunResponse); await vrt.track(testRun); expect(vrt["submitTestResult"]).toHaveBeenCalledWith(testRun); + expect(mockedTestRunResult).toHaveBeenCalledWith( + testRunResponse, + "http://localhost:4200" + ); }); - describe.each<[TestRunStatus.new | TestRunStatus.unresolved, string]>([ - [TestRunStatus.new, "No baseline: "], - [TestRunStatus.unresolved, "Difference found: "], + describe.each<[TestStatus.new | TestStatus.unresolved, string]>([ + [TestStatus.new, "No baseline: "], + [TestStatus.unresolved, "Difference found: "], ])("should track error", (status, expectedMessage) => { - const testRunResultMock: TestRunResult = { + const testRunResponseMock: TestRunResponse = { url: "http://foo.bar", - status: TestRunStatus.ok, + status: TestStatus.ok, pixelMisMatchCount: 12, diffPercent: 0.12, diffTollerancePercent: 0, + id: "some id", + imageName: "imageName", + merge: false, }; beforeEach(() => { - testRunResultMock.status = status; + testRunResponseMock.status = status; }); it(`disabled soft assert should throw exception if status ${status}`, async () => { vrt["config"].enableSoftAssert = false; vrt["submitTestResult"] = jest .fn() - .mockResolvedValueOnce(testRunResultMock); + .mockResolvedValueOnce(testRunResponseMock); await expect(vrt.track(testRun)).rejects.toThrowError( - new Error(expectedMessage.concat(testRunResultMock.url)) + new Error(expectedMessage.concat(testRunResponseMock.url)) ); }); @@ -164,12 +182,12 @@ describe("VisualRegressionTracker", () => { vrt["config"].enableSoftAssert = true; vrt["submitTestResult"] = jest .fn() - .mockResolvedValueOnce(testRunResultMock); + .mockResolvedValueOnce(testRunResponseMock); await vrt.track(testRun); expect(console.error).toHaveBeenCalledWith( - expectedMessage.concat(testRunResultMock.url) + expectedMessage.concat(testRunResponseMock.url) ); }); }); @@ -261,12 +279,15 @@ describe("VisualRegressionTracker", () => { describe("submitTestResults", () => { it("should submit test run", async () => { - const testRunResult: TestRunResult = { + const testRunResponse: TestRunResponse = { url: "url", - status: TestRunStatus.unresolved, + status: TestStatus.unresolved, pixelMisMatchCount: 12, diffPercent: 0.12, diffTollerancePercent: 0, + id: "some id", + imageName: "imageName", + merge: false, }; const testRun: TestRun = { name: "name", @@ -280,11 +301,11 @@ describe("VisualRegressionTracker", () => { const projectId = "asd"; vrt["buildId"] = buildId; vrt["projectId"] = projectId; - mockedAxios.post.mockResolvedValueOnce({ data: testRunResult }); + mockedAxios.post.mockResolvedValueOnce({ data: testRunResponse }); const result = await vrt["submitTestResult"](testRun); - expect(result).toBe(testRunResult); + expect(result).toBe(testRunResponse); expect(mockedAxios.post).toHaveBeenCalledWith( `${config.apiUrl}/test-runs`, { diff --git a/lib/visualRegressionTracker.ts b/lib/visualRegressionTracker.ts index 4988e7e..7b3158d 100644 --- a/lib/visualRegressionTracker.ts +++ b/lib/visualRegressionTracker.ts @@ -1,4 +1,11 @@ -import { Config, Build, TestRun, TestRunResult, TestRunStatus } from "./types"; +import { + Config, + Build, + TestRun, + TestRunResponse, + TestStatus, +} from "./types"; +import TestRunResult from "./testRunResult" import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"; export class VisualRegressionTracker { @@ -50,7 +57,7 @@ export class VisualRegressionTracker { .catch(this.handleException); } - private async submitTestResult(test: TestRun): Promise { + private async submitTestResult(test: TestRun): Promise { if (!this.isStarted()) { throw new Error("Visual Regression Tracker has not been started"); } @@ -89,17 +96,17 @@ export class VisualRegressionTracker { } } - async track(test: TestRun) { - const result = await this.submitTestResult(test); + async track(test: TestRun): Promise { + const testRunResponse = await this.submitTestResult(test); let errorMessage: string | undefined; - switch (result.status) { - case TestRunStatus.new: { - errorMessage = `No baseline: ${result.url}`; + switch (testRunResponse.status) { + case TestStatus.new: { + errorMessage = `No baseline: ${testRunResponse.url}`; break; } - case TestRunStatus.unresolved: { - errorMessage = `Difference found: ${result.url}`; + case TestStatus.unresolved: { + errorMessage = `Difference found: ${testRunResponse.url}`; } } @@ -111,5 +118,7 @@ export class VisualRegressionTracker { throw new Error(errorMessage); } } + + return new TestRunResult(testRunResponse, this.config.apiUrl); } } From 140a2535a13ca9e97399cb9a76d664e43dbcd83c Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 10 Oct 2020 22:15:26 +0300 Subject: [PATCH 2/5] refactoring --- lib/index.ts | 1 + lib/types/index.ts | 2 +- lib/types/{build.ts => response/buildResponse.ts} | 2 +- lib/visualRegressionTracker.spec.ts | 6 +++--- lib/visualRegressionTracker.ts | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) rename lib/types/{build.ts => response/buildResponse.ts} (52%) diff --git a/lib/index.ts b/lib/index.ts index 45f560e..44032d4 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,2 +1,3 @@ export * from "./visualRegressionTracker"; +export * from "./testRunResult"; export * from "./types"; diff --git a/lib/types/index.ts b/lib/types/index.ts index 538fc5c..47b797e 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -1,4 +1,4 @@ -export * from "./build"; +export * from "./response/buildResponse"; export * from "./config"; export * from "./testRun"; export * from "./response/testRunResponse"; diff --git a/lib/types/build.ts b/lib/types/response/buildResponse.ts similarity index 52% rename from lib/types/build.ts rename to lib/types/response/buildResponse.ts index e6f6638..2e972ab 100644 --- a/lib/types/build.ts +++ b/lib/types/response/buildResponse.ts @@ -1,4 +1,4 @@ -export interface Build { +export interface BuildResponse { id: string; projectId: string; } diff --git a/lib/visualRegressionTracker.spec.ts b/lib/visualRegressionTracker.spec.ts index c00f1f7..25a1647 100644 --- a/lib/visualRegressionTracker.spec.ts +++ b/lib/visualRegressionTracker.spec.ts @@ -1,5 +1,5 @@ import { VisualRegressionTracker } from "./visualRegressionTracker"; -import { Config, Build, TestRun, TestRunResponse, TestStatus } from "./types"; +import { Config, BuildResponse, TestRun, TestRunResponse, TestStatus } from "./types"; import { mocked } from "ts-jest/utils"; import TestRunResult from "./testRunResult"; import axios, { AxiosError, AxiosResponse } from "axios"; @@ -197,7 +197,7 @@ describe("VisualRegressionTracker", () => { test("should start build", async () => { const buildId = "1312"; const projectId = "asd"; - const build: Build = { + const build: BuildResponse = { id: buildId, projectId: projectId, }; @@ -358,7 +358,7 @@ describe("VisualRegressionTracker", () => { }); test("handleResponse", async () => { - const build: Build = { + const build: BuildResponse = { id: "id", projectId: "projectId", }; diff --git a/lib/visualRegressionTracker.ts b/lib/visualRegressionTracker.ts index 7b3158d..96723c6 100644 --- a/lib/visualRegressionTracker.ts +++ b/lib/visualRegressionTracker.ts @@ -1,6 +1,6 @@ import { Config, - Build, + BuildResponse, TestRun, TestRunResponse, TestStatus, @@ -33,7 +33,7 @@ export class VisualRegressionTracker { project: this.config.project, }; - const build: Build = await axios + const build: BuildResponse = await axios .post(`${this.config.apiUrl}/builds`, data, this.axiosConfig) .then(this.handleResponse) .catch(this.handleException); From a914b31847d2d9b388940c6ec97d3bec0e7b1633 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 10 Oct 2020 22:16:11 +0300 Subject: [PATCH 3/5] Update testRunResult.spec.ts --- lib/testRunResult.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/testRunResult.spec.ts b/lib/testRunResult.spec.ts index 80498fa..230c01c 100644 --- a/lib/testRunResult.spec.ts +++ b/lib/testRunResult.spec.ts @@ -18,8 +18,8 @@ describe("TestRunResult", () => { expect(result.testRunResponse).toBe(testRunResponse); expect(result.imageUrl).toBe("http://localhost/imageName"); - expect(result.diffUrl).toBe(undefined); - expect(result.baselineUrl).toBe(undefined); + expect(result.diffUrl).toBeUndefined(); + expect(result.baselineUrl).toBeUndefined(); }); it("all image", () => { From fd21f0e7e48cb1a415dc91f49f74f38be8a21886 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 10 Oct 2020 22:42:17 +0300 Subject: [PATCH 4/5] refactoring --- lib/types/index.ts | 2 +- lib/visualRegressionTracker.spec.ts | 94 +++++++++++++++-------------- lib/visualRegressionTracker.ts | 11 +++- 3 files changed, 57 insertions(+), 50 deletions(-) diff --git a/lib/types/index.ts b/lib/types/index.ts index 47b797e..cb37d9b 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -1,5 +1,5 @@ -export * from "./response/buildResponse"; export * from "./config"; export * from "./testRun"; +export * from "./response/buildResponse"; export * from "./response/testRunResponse"; export * from "./testStatus"; diff --git a/lib/visualRegressionTracker.spec.ts b/lib/visualRegressionTracker.spec.ts index 25a1647..91738e9 100644 --- a/lib/visualRegressionTracker.spec.ts +++ b/lib/visualRegressionTracker.spec.ts @@ -1,5 +1,11 @@ import { VisualRegressionTracker } from "./visualRegressionTracker"; -import { Config, BuildResponse, TestRun, TestRunResponse, TestStatus } from "./types"; +import { + Config, + BuildResponse, + TestRun, + TestRunResponse, + TestStatus, +} from "./types"; import { mocked } from "ts-jest/utils"; import TestRunResult from "./testRunResult"; import axios, { AxiosError, AxiosResponse } from "axios"; @@ -137,60 +143,17 @@ describe("VisualRegressionTracker", () => { vrt["submitTestResult"] = jest .fn() .mockResolvedValueOnce(testRunResponse); + vrt["processTestRun"] = jest.fn(); await vrt.track(testRun); expect(vrt["submitTestResult"]).toHaveBeenCalledWith(testRun); + expect(vrt["processTestRun"]).toHaveBeenCalledWith(testRunResponse); expect(mockedTestRunResult).toHaveBeenCalledWith( testRunResponse, "http://localhost:4200" ); }); - - describe.each<[TestStatus.new | TestStatus.unresolved, string]>([ - [TestStatus.new, "No baseline: "], - [TestStatus.unresolved, "Difference found: "], - ])("should track error", (status, expectedMessage) => { - const testRunResponseMock: TestRunResponse = { - url: "http://foo.bar", - status: TestStatus.ok, - pixelMisMatchCount: 12, - diffPercent: 0.12, - diffTollerancePercent: 0, - id: "some id", - imageName: "imageName", - merge: false, - }; - - beforeEach(() => { - testRunResponseMock.status = status; - }); - - it(`disabled soft assert should throw exception if status ${status}`, async () => { - vrt["config"].enableSoftAssert = false; - vrt["submitTestResult"] = jest - .fn() - .mockResolvedValueOnce(testRunResponseMock); - - await expect(vrt.track(testRun)).rejects.toThrowError( - new Error(expectedMessage.concat(testRunResponseMock.url)) - ); - }); - - it(`enabled soft assert should log error if status ${status}`, async () => { - console.error = jest.fn(); - vrt["config"].enableSoftAssert = true; - vrt["submitTestResult"] = jest - .fn() - .mockResolvedValueOnce(testRunResponseMock); - - await vrt.track(testRun); - - expect(console.error).toHaveBeenCalledWith( - expectedMessage.concat(testRunResponseMock.url) - ); - }); - }); }); describe("start", () => { @@ -396,4 +359,43 @@ describe("VisualRegressionTracker", () => { ); }); }); + + describe.each<[TestStatus.new | TestStatus.unresolved, string]>([ + [TestStatus.new, "No baseline: "], + [TestStatus.unresolved, "Difference found: "], + ])("processTestRun", (status, expectedMessage) => { + const testRunResponse: TestRunResponse = { + url: "http://foo.bar", + status: TestStatus.ok, + pixelMisMatchCount: 12, + diffPercent: 0.12, + diffTollerancePercent: 0, + id: "some id", + imageName: "imageName", + merge: false, + }; + + beforeEach(() => { + testRunResponse.status = status; + }); + + it(`disabled soft assert should throw exception if status ${status}`, () => { + vrt["config"].enableSoftAssert = false; + + expect(() => vrt["processTestRun"](testRunResponse)).toThrowError( + new Error(expectedMessage.concat(testRunResponse.url)) + ); + }); + + it(`enabled soft assert should log error if status ${status}`, () => { + console.error = jest.fn(); + vrt["config"].enableSoftAssert = true; + + vrt["processTestRun"](testRunResponse); + + expect(console.error).toHaveBeenCalledWith( + expectedMessage.concat(testRunResponse.url) + ); + }); + }); }); diff --git a/lib/visualRegressionTracker.ts b/lib/visualRegressionTracker.ts index 96723c6..852adc1 100644 --- a/lib/visualRegressionTracker.ts +++ b/lib/visualRegressionTracker.ts @@ -5,7 +5,7 @@ import { TestRunResponse, TestStatus, } from "./types"; -import TestRunResult from "./testRunResult" +import TestRunResult from "./testRunResult"; import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"; export class VisualRegressionTracker { @@ -99,6 +99,12 @@ export class VisualRegressionTracker { async track(test: TestRun): Promise { const testRunResponse = await this.submitTestResult(test); + this.processTestRun(testRunResponse); + + return new TestRunResult(testRunResponse, this.config.apiUrl); + } + + private processTestRun(testRunResponse: TestRunResponse): void { let errorMessage: string | undefined; switch (testRunResponse.status) { case TestStatus.new: { @@ -107,6 +113,7 @@ export class VisualRegressionTracker { } case TestStatus.unresolved: { errorMessage = `Difference found: ${testRunResponse.url}`; + break; } } @@ -118,7 +125,5 @@ export class VisualRegressionTracker { throw new Error(errorMessage); } } - - return new TestRunResult(testRunResponse, this.config.apiUrl); } } From 81d6ec27555a08095e32a28aa905562f3faa525f Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 10 Oct 2020 22:42:44 +0300 Subject: [PATCH 5/5] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91c66ad..310735f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@visual-regression-tracker/sdk-js", - "version": "4.2.0", + "version": "4.3.0", "description": "", "main": "dist/index.js", "types": "dist/index.d.ts",