From 076d3512d974b479b2ed194fa1480054b9821f5e Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Mon, 13 Jul 2020 22:17:46 +0200 Subject: [PATCH 01/16] merge added --- package-lock.json | 33 +++++---- package.json | 4 +- .../README.md | 40 +++++++---- .../schema.prisma | 5 +- .../steps.json | 32 +++++++++ prisma/migrations/migrate.lock | 2 +- prisma/package-lock.json | 68 +++++++++++++++++-- prisma/package.json | 9 ++- prisma/schema.prisma | 5 +- src/test-runs/dto/create-test-request.dto.ts | 7 +- src/test-runs/dto/testRunResult.dto.ts | 26 +++---- src/test-runs/test-runs.controller.ts | 21 ++++-- src/test-runs/test-runs.service.ts | 57 +++++++++++++++- 13 files changed, 244 insertions(+), 65 deletions(-) rename prisma/migrations/{20200711125803-branch-strategy => 20200713221657-branch-strategy}/README.md (55%) rename prisma/migrations/{20200711125803-branch-strategy => 20200713221657-branch-strategy}/schema.prisma (95%) rename prisma/migrations/{20200711125803-branch-strategy => 20200713221657-branch-strategy}/steps.json (77%) diff --git a/package-lock.json b/package-lock.json index 549b6ca7..c53030a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1333,15 +1333,18 @@ } }, "@prisma/cli": { - "version": "2.0.0-beta.6", - "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.0.0-beta.6.tgz", - "integrity": "sha512-qawcjLN5c26T+IVZij5WjQocfsV6b1BtJIL3CqMQfMkDCNGo/zXsu+NB+uyZ+QRqctEuJQ36lemnY44+k6L8XA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.2.1.tgz", + "integrity": "sha512-1pAk1KkV+I9oOzgMS/eWFsz3r/RPckDhCez5NV96zxHV/tzBCHmnfhIv1rD6Q12nj7fSjWlYUqAwprVcWCVtTg==", "dev": true }, "@prisma/client": { - "version": "2.0.0-beta.6", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.0.0-beta.6.tgz", - "integrity": "sha512-08AokCpMzL6SdxQVfShD7937t+sHntr6FJBpVKq5E/UFPVh0B2lUwU9YrA/MIAdRpGMg1wA+rdwRivtFIs5Law==" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.2.1.tgz", + "integrity": "sha512-DDZwvO1VxcztIiCzr4S3K0YeAv4+or+Q9ZEjkUgRtrhxBs3/VEBSYh4+0PhMZ/0HZx4QXYZzxzp6tg+54PGpAQ==", + "requires": { + "pkg-up": "^3.1.0" + } }, "@schematics/schematics": { "version": "0.901.0", @@ -4838,7 +4841,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, "requires": { "locate-path": "^3.0.0" } @@ -7533,7 +7535,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, "requires": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -8554,7 +8555,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "requires": { "p-try": "^2.0.0" } @@ -8563,7 +8563,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, "requires": { "p-limit": "^2.0.0" } @@ -8571,8 +8570,7 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "packet-reader": { "version": "1.0.0", @@ -8745,8 +8743,7 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" }, "path-is-absolute": { "version": "1.0.1", @@ -8926,6 +8923,14 @@ "find-up": "^3.0.0" } }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + } + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", diff --git a/package.json b/package.json index 085d67a5..84742ecb 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@nestjs/platform-socket.io": "^7.1.3", "@nestjs/swagger": "^4.5.1", "@nestjs/websockets": "^7.1.3", - "@prisma/client": "^2.0.0-beta.6", + "@prisma/client": "^2.2.1", "bcryptjs": "^2.4.3", "class-transformer": "^0.2.3", "class-validator": "^0.11.1", @@ -52,7 +52,7 @@ "@nestjs/cli": "^7.0.0", "@nestjs/schematics": "^7.0.0", "@nestjs/testing": "^7.0.0", - "@prisma/cli": "^2.0.0-beta.6", + "@prisma/cli": "^2.2.1", "@types/bcryptjs": "^2.4.2", "@types/express": "^4.17.3", "@types/jest": "25.1.4", diff --git a/prisma/migrations/20200711125803-branch-strategy/README.md b/prisma/migrations/20200713221657-branch-strategy/README.md similarity index 55% rename from prisma/migrations/20200711125803-branch-strategy/README.md rename to prisma/migrations/20200713221657-branch-strategy/README.md index 35b7a575..2b499812 100644 --- a/prisma/migrations/20200711125803-branch-strategy/README.md +++ b/prisma/migrations/20200713221657-branch-strategy/README.md @@ -1,24 +1,19 @@ -# Migration `20200711125803-branch-strategy` +# Migration `20200713221657-branch-strategy` -This migration has been generated by Pavel Strunkin at 7/11/2020, 12:58:03 PM. +This migration has been generated by Pavel Strunkin at 7/13/2020, 10:16:57 PM. You can check out the [state of the schema](./schema.prisma) after the migration. ## Database Steps ```sql -ALTER TABLE "public"."Project" ADD COLUMN "mainBranchName" text NOT NULL DEFAULT E'master'; -ALTER TABLE "public"."TestRun" ADD COLUMN "baselineBranchName" text , -ADD COLUMN "branchName" text NOT NULL DEFAULT E'master'; - -ALTER TABLE "public"."TestVariation" ADD COLUMN "branchName" text NOT NULL DEFAULT E'master'; ``` ## Changes ```diff diff --git schema.prisma schema.prisma -migration 20200707182652-project-name-unique-constraint..20200711125803-branch-strategy +migration 20200707182652-project-name-unique-constraint..20200713221657-branch-strategy --- datamodel.dml +++ datamodel.dml @@ -3,9 +3,9 @@ @@ -26,7 +21,7 @@ migration 20200707182652-project-name-unique-constraint..20200711125803-branch-s datasource db { provider = "postgresql" - url = "***" -+ url = env("DATABASE_URL") ++ url = "***" } model Build { id String @default(uuid()) @id @@ -39,7 +34,17 @@ migration 20200707182652-project-name-unique-constraint..20200711125803-branch-s testVariations TestVariation[] updatedAt DateTime @updatedAt createdAt DateTime @default(now()) -@@ -55,8 +56,10 @@ +@@ -43,8 +44,9 @@ + buildId String + build Build @relation(fields: [buildId], references: [id]) + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) ++ merge Boolean @default(false) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + // Test variation data + name String @default("") +@@ -55,13 +57,16 @@ baselineName String? ignoreAreas String @default("[]") comment String? @@ -49,15 +54,22 @@ migration 20200707182652-project-name-unique-constraint..20200711125803-branch-s } model TestVariation { id String @default(uuid()) @id -@@ -71,8 +74,9 @@ - project Project @relation(fields: [projectId], references: [id]) - testRuns TestRun[] + name String ++ branchName String @default("master") + browser String? + device String? + os String? + viewport String? +@@ -73,8 +78,10 @@ baselines Baseline[] comment String? -+ branchName String @default("master") updatedAt DateTime @updatedAt createdAt DateTime @default(now()) ++ ++ // @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") } + model Baseline { + id String @default(uuid()) @id ``` diff --git a/prisma/migrations/20200711125803-branch-strategy/schema.prisma b/prisma/migrations/20200713221657-branch-strategy/schema.prisma similarity index 95% rename from prisma/migrations/20200711125803-branch-strategy/schema.prisma rename to prisma/migrations/20200713221657-branch-strategy/schema.prisma index 87feafce..c76b8f51 100644 --- a/prisma/migrations/20200711125803-branch-strategy/schema.prisma +++ b/prisma/migrations/20200713221657-branch-strategy/schema.prisma @@ -45,6 +45,7 @@ model TestRun { build Build @relation(fields: [buildId], references: [id]) testVariationId String testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + merge Boolean @default(false) updatedAt DateTime @updatedAt createdAt DateTime @default(now()) // Test variation data @@ -64,6 +65,7 @@ model TestRun { model TestVariation { id String @default(uuid()) @id name String + branchName String @default("master") browser String? device String? os String? @@ -75,9 +77,10 @@ model TestVariation { testRuns TestRun[] baselines Baseline[] comment String? - branchName String @default("master") updatedAt DateTime @updatedAt createdAt DateTime @default(now()) + + // @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") } model Baseline { diff --git a/prisma/migrations/20200711125803-branch-strategy/steps.json b/prisma/migrations/20200713221657-branch-strategy/steps.json similarity index 77% rename from prisma/migrations/20200711125803-branch-strategy/steps.json rename to prisma/migrations/20200713221657-branch-strategy/steps.json index d12b143b..3fa55bc9 100644 --- a/prisma/migrations/20200711125803-branch-strategy/steps.json +++ b/prisma/migrations/20200713221657-branch-strategy/steps.json @@ -33,6 +33,38 @@ "argument": "", "value": "\"master\"" }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "merge", + "type": "Boolean", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "TestRun", + "field": "merge" + }, + "directive": "default" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "TestRun", + "field": "merge" + }, + "directive": "default" + }, + "argument": "", + "value": "false" + }, { "tag": "CreateField", "model": "TestRun", diff --git a/prisma/migrations/migrate.lock b/prisma/migrations/migrate.lock index 4d670fd2..97113db4 100644 --- a/prisma/migrations/migrate.lock +++ b/prisma/migrations/migrate.lock @@ -5,4 +5,4 @@ 20200526195312-approved-test-status-added 20200627134248-comment-added 20200707182652-project-name-unique-constraint -20200711125803-branch-strategy \ No newline at end of file +20200713221657-branch-strategy \ No newline at end of file diff --git a/prisma/package-lock.json b/prisma/package-lock.json index 439a5b88..cf8a3ef1 100644 --- a/prisma/package-lock.json +++ b/prisma/package-lock.json @@ -1,19 +1,22 @@ { "name": "vrt-migration", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { "@prisma/cli": { - "version": "2.0.0-beta.6", - "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.0.0-beta.6.tgz", - "integrity": "sha512-qawcjLN5c26T+IVZij5WjQocfsV6b1BtJIL3CqMQfMkDCNGo/zXsu+NB+uyZ+QRqctEuJQ36lemnY44+k6L8XA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.2.1.tgz", + "integrity": "sha512-1pAk1KkV+I9oOzgMS/eWFsz3r/RPckDhCez5NV96zxHV/tzBCHmnfhIv1rD6Q12nj7fSjWlYUqAwprVcWCVtTg==", "dev": true }, "@prisma/client": { - "version": "2.0.0-beta.6", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.0.0-beta.6.tgz", - "integrity": "sha512-08AokCpMzL6SdxQVfShD7937t+sHntr6FJBpVKq5E/UFPVh0B2lUwU9YrA/MIAdRpGMg1wA+rdwRivtFIs5Law==" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.2.1.tgz", + "integrity": "sha512-DDZwvO1VxcztIiCzr4S3K0YeAv4+or+Q9ZEjkUgRtrhxBs3/VEBSYh4+0PhMZ/0HZx4QXYZzxzp6tg+54PGpAQ==", + "requires": { + "pkg-up": "^3.1.0" + } }, "@types/bcryptjs": { "version": "2.4.2", @@ -65,12 +68,63 @@ "resolved": "https://registry.npmjs.org/encode32/-/encode32-1.1.0.tgz", "integrity": "sha1-DFS0X7MUrVUC48Iwy5Ws3F5c0d0=" }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/prisma/package.json b/prisma/package.json index a75bd155..4780b8f3 100644 --- a/prisma/package.json +++ b/prisma/package.json @@ -5,18 +5,17 @@ "author": "", "private": true, "license": "UNLICENSED", - "scripts": { - }, + "scripts": {}, "dependencies": { - "@prisma/client": "^2.0.0-beta.6", + "@prisma/client": "^2.2.1", "bcryptjs": "^2.4.3", "uuid-apikey": "^1.4.6" }, "devDependencies": { - "ts-node": "^8.8.2", - "@prisma/cli": "^2.0.0-beta.6", + "@prisma/cli": "^2.2.1", "@types/bcryptjs": "^2.4.2", "@types/uuid-apikey": "^1.4.0", + "ts-node": "^8.8.2", "typescript": "^3.7.4" } } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 78c0bb0b..276c92e1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -45,6 +45,7 @@ model TestRun { build Build @relation(fields: [buildId], references: [id]) testVariationId String testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + merge Boolean @default(false) updatedAt DateTime @updatedAt createdAt DateTime @default(now()) // Test variation data @@ -64,6 +65,7 @@ model TestRun { model TestVariation { id String @default(uuid()) @id name String + branchName String @default("master") browser String? device String? os String? @@ -75,9 +77,10 @@ model TestVariation { testRuns TestRun[] baselines Baseline[] comment String? - branchName String @default("master") updatedAt DateTime @updatedAt createdAt DateTime @default(now()) + + // @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") } model Baseline { diff --git a/src/test-runs/dto/create-test-request.dto.ts b/src/test-runs/dto/create-test-request.dto.ts index 44c0c0d2..c11ae547 100644 --- a/src/test-runs/dto/create-test-request.dto.ts +++ b/src/test-runs/dto/create-test-request.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsOptional, IsBase64, IsUUID, IsNumber } from 'class-validator'; +import { IsOptional, IsBase64, IsUUID, IsNumber, IsBoolean } from 'class-validator'; import { BaselineDataDto } from '../../shared/dto/baseline-data.dto'; export class CreateTestRequestDto extends BaselineDataDto { @@ -19,4 +19,9 @@ export class CreateTestRequestDto extends BaselineDataDto { @IsOptional() @IsNumber() diffTollerancePercent?: number; + + @ApiProperty() + @IsBoolean() + @IsOptional() + merge?: boolean; } diff --git a/src/test-runs/dto/testRunResult.dto.ts b/src/test-runs/dto/testRunResult.dto.ts index 18bcb34d..5e854618 100644 --- a/src/test-runs/dto/testRunResult.dto.ts +++ b/src/test-runs/dto/testRunResult.dto.ts @@ -1,23 +1,25 @@ import { TestRun, TestStatus, TestVariation } from '@prisma/client'; export class TestRunResultDto { - id: string - imageName: string - diffName?: string - diffPercent: number + id: string; + imageName: string; + diffName?: string; + diffPercent: number; diffTollerancePercent?: number; pixelMisMatchCount?: number; status: TestStatus; url: string; + merge: boolean; constructor(testRun: TestRun, testVariation: TestVariation) { - this.id = testRun.id - this.imageName = testRun.imageName - this.diffName = testRun.diffName - this.diffPercent = testRun.diffPercent - this.diffTollerancePercent = testRun.diffTollerancePercent - this.pixelMisMatchCount = testRun.pixelMisMatchCount - this.status = testRun.status - this.url = `${process.env.APP_FRONTEND_URL}/${testVariation.projectId}?buildId=${testRun.buildId}&testId=${testRun.id}` + this.id = testRun.id; + this.imageName = testRun.imageName; + this.diffName = testRun.diffName; + this.diffPercent = testRun.diffPercent; + this.diffTollerancePercent = testRun.diffTollerancePercent; + this.pixelMisMatchCount = testRun.pixelMisMatchCount; + this.status = testRun.status; + this.merge = testRun.merge; + this.url = `${process.env.APP_FRONTEND_URL}/${testVariation.projectId}?buildId=${testRun.buildId}&testId=${testRun.id}`; } } diff --git a/src/test-runs/test-runs.controller.ts b/src/test-runs/test-runs.controller.ts index e2e226ef..4350c6a5 100644 --- a/src/test-runs/test-runs.controller.ts +++ b/src/test-runs/test-runs.controller.ts @@ -30,12 +30,13 @@ export class TestRunsController { return this.testRunsService.recalculateDiff(id); } - @Get('approve/:id') - @ApiParam({ name: 'id', required: true }) + @Get('approve') + @ApiQuery({ name: 'id', required: true }) + @ApiQuery({ name: 'merge', required: false }) @ApiBearerAuth() @UseGuards(JwtAuthGuard) - approveTestRun(@Param('id', new ParseUUIDPipe()) id: string): Promise { - return this.testRunsService.approve(id); + approveTestRun(@Query('id', new ParseUUIDPipe()) id: string, @Query('merge') merge: boolean): Promise { + return this.testRunsService.approve(id, merge); } @Get('reject/:id') @@ -80,4 +81,16 @@ export class TestRunsController { postTestRun(@Body() createTestRequestDto: CreateTestRequestDto): Promise { return this.testRunsService.postTestRun(createTestRequestDto); } + + @Get('merge') + @ApiQuery({ name: 'projectId', required: true }) + @ApiQuery({ name: 'branchName', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + merge( + @Query('projectId', new ParseUUIDPipe()) projectId: string, + @Query('branchName') branchName: string + ): Promise { + return this.testRunsService.merge(projectId, branchName); + } } diff --git a/src/test-runs/test-runs.service.ts b/src/test-runs/test-runs.service.ts index dc2fbcb7..688c030e 100644 --- a/src/test-runs/test-runs.service.ts +++ b/src/test-runs/test-runs.service.ts @@ -5,7 +5,7 @@ import { CreateTestRequestDto } from './dto/create-test-request.dto'; import { IgnoreAreaDto } from './dto/ignore-area.dto'; import { StaticService } from '../shared/static/static.service'; import { PrismaService } from '../prisma/prisma.service'; -import { TestRun, TestStatus, TestVariation } from '@prisma/client'; +import { TestRun, TestStatus, TestVariation, Project, Build } from '@prisma/client'; import { DiffResult } from './diffResult'; import { EventsGateway } from '../events/events.gateway'; import { CommentDto } from '../shared/dto/comment.dto'; @@ -44,6 +44,56 @@ export class TestRunsService { }); } + async merge(projectId: string, branchName: string): Promise { + const project: Project = await this.prismaService.project.findOne({ where: { id: projectId } }); + + // create build + const build: Build = await this.prismaService.build.create({ + data: { + branchName: project.mainBranchName, + project: { + connect: { + id: project.id, + }, + }, + }, + }); + this.eventsGateway.buildCreated(new BuildDto(build)); + + // find side branch variations + const testVariations: TestVariation[] = await this.prismaService.testVariation.findMany({ + where: { projectId, branchName }, + }); + + // compare to main branch variations + await Promise.all( + testVariations.map(async sideBranchTestVariation => { + const baseline = this.staticService.getImage(sideBranchTestVariation.baselineName); + if (baseline) { + // get main branch variation + const baselineData = convertBaselineDataToQuery({ + ...sideBranchTestVariation, + branchName: project.mainBranchName, + }); + const mainBranchTestVariation = await this.testVariationService.findOrCreate(projectId, baselineData); + + // get side branch request + const imageBase64 = PNG.sync.write(baseline).toString('base64'); + const createTestRequestDto: CreateTestRequestDto = { + ...sideBranchTestVariation, + projectId, + buildId: build.id, + imageBase64, + diffTollerancePercent: 0, + merge: true, + }; + + return this.create(mainBranchTestVariation, createTestRequestDto); + } + }) + ); + } + async postTestRun(createTestRequestDto: CreateTestRequestDto): Promise { const baselineData = convertBaselineDataToQuery(createTestRequestDto); @@ -81,14 +131,14 @@ export class TestRunsService { this.eventsGateway.buildUpdated(buildDto); } - async approve(id: string): Promise { + async approve(id: string, merge: boolean): Promise { const testRun = await this.findOne(id); // save new baseline const baseline = this.staticService.getImage(testRun.imageName); const baselineName = this.staticService.saveImage('baseline', PNG.sync.write(baseline)); let testRunUpdated: TestRun; - if (testRun.branchName === testRun.baselineBranchName) { + if (merge || testRun.branchName === testRun.baselineBranchName) { testRunUpdated = await this.prismaService.testRun.update({ where: { id }, data: { @@ -219,6 +269,7 @@ export class TestRunsService { comment: testVariation.comment, diffTollerancePercent: createTestRequestDto.diffTollerancePercent, branchName: createTestRequestDto.branchName, + merge: createTestRequestDto.merge, status: TestStatus.new, }, }); From 803ab8d5c77fc1f9349e07a337fd6c4db710a16c Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Mon, 13 Jul 2020 22:22:35 +0200 Subject: [PATCH 02/16] variation unique constraint added --- .../20200713221657-branch-strategy/README.md | 75 -------- .../20200713222147-branch-strategy/README.md | 180 ++++++++++++++++++ .../schema.prisma | 2 +- .../steps.json | 20 ++ prisma/migrations/migrate.lock | 2 +- prisma/schema.prisma | 2 +- 6 files changed, 203 insertions(+), 78 deletions(-) delete mode 100644 prisma/migrations/20200713221657-branch-strategy/README.md create mode 100644 prisma/migrations/20200713222147-branch-strategy/README.md rename prisma/migrations/{20200713221657-branch-strategy => 20200713222147-branch-strategy}/schema.prisma (97%) rename prisma/migrations/{20200713221657-branch-strategy => 20200713222147-branch-strategy}/steps.json (86%) diff --git a/prisma/migrations/20200713221657-branch-strategy/README.md b/prisma/migrations/20200713221657-branch-strategy/README.md deleted file mode 100644 index 2b499812..00000000 --- a/prisma/migrations/20200713221657-branch-strategy/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Migration `20200713221657-branch-strategy` - -This migration has been generated by Pavel Strunkin at 7/13/2020, 10:16:57 PM. -You can check out the [state of the schema](./schema.prisma) after the migration. - -## Database Steps - -```sql - -``` - -## Changes - -```diff -diff --git schema.prisma schema.prisma -migration 20200707182652-project-name-unique-constraint..20200713221657-branch-strategy ---- datamodel.dml -+++ datamodel.dml -@@ -3,9 +3,9 @@ - } - datasource db { - provider = "postgresql" -- url = "***" -+ url = "***" - } - model Build { - id String @default(uuid()) @id -@@ -23,8 +23,9 @@ - model Project { - id String @default(uuid()) @id - name String -+ mainBranchName String @default("master") - builds Build[] - testVariations TestVariation[] - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -@@ -43,8 +44,9 @@ - buildId String - build Build @relation(fields: [buildId], references: [id]) - testVariationId String - testVariation TestVariation @relation(fields: [testVariationId], references: [id]) -+ merge Boolean @default(false) - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) - // Test variation data - name String @default("") -@@ -55,13 +57,16 @@ - baselineName String? - ignoreAreas String @default("[]") - comment String? - baseline Baseline? -+ branchName String @default("master") -+ baselineBranchName String? - } - model TestVariation { - id String @default(uuid()) @id - name String -+ branchName String @default("master") - browser String? - device String? - os String? - viewport String? -@@ -73,8 +78,10 @@ - baselines Baseline[] - comment String? - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -+ -+ // @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") - } - model Baseline { - id String @default(uuid()) @id -``` - - diff --git a/prisma/migrations/20200713222147-branch-strategy/README.md b/prisma/migrations/20200713222147-branch-strategy/README.md new file mode 100644 index 00000000..71d95908 --- /dev/null +++ b/prisma/migrations/20200713222147-branch-strategy/README.md @@ -0,0 +1,180 @@ +# Migration `20200713222147-branch-strategy` + +This migration has been generated by Pavel Strunkin at 7/13/2020, 10:21:47 PM. +You can check out the [state of the schema](./schema.prisma) after the migration. + +## Database Steps + +```sql +CREATE TYPE "TestStatus" AS ENUM ('failed', 'new', 'ok', 'unresolved', 'approved'); + +CREATE TABLE "public"."Build" ( +"id" text NOT NULL , +"number" integer , +"branchName" text , +"status" text , +"projectId" text NOT NULL , +"updatedAt" timestamp(3) NOT NULL , +"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +"userId" text , + PRIMARY KEY ("id")) + +CREATE TABLE "public"."Project" ( +"id" text NOT NULL , +"name" text NOT NULL , +"mainBranchName" text NOT NULL DEFAULT E'master', +"updatedAt" timestamp(3) NOT NULL , +"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id")) + +CREATE TABLE "public"."TestRun" ( +"id" text NOT NULL , +"imageName" text NOT NULL , +"diffName" text , +"diffPercent" Decimal(65,30) , +"diffTollerancePercent" Decimal(65,30) NOT NULL DEFAULT 1.0, +"pixelMisMatchCount" integer , +"status" "TestStatus" NOT NULL , +"buildId" text NOT NULL , +"testVariationId" text NOT NULL , +"merge" boolean NOT NULL DEFAULT false, +"updatedAt" timestamp(3) NOT NULL , +"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +"name" text NOT NULL DEFAULT E'', +"browser" text , +"device" text , +"os" text , +"viewport" text , +"baselineName" text , +"ignoreAreas" text NOT NULL DEFAULT E'[]', +"comment" text , +"branchName" text NOT NULL DEFAULT E'master', +"baselineBranchName" text , + PRIMARY KEY ("id")) + +CREATE TABLE "public"."TestVariation" ( +"id" text NOT NULL , +"name" text NOT NULL , +"branchName" text NOT NULL DEFAULT E'master', +"browser" text , +"device" text , +"os" text , +"viewport" text , +"baselineName" text , +"ignoreAreas" text NOT NULL DEFAULT E'[]', +"projectId" text NOT NULL , +"comment" text , +"updatedAt" timestamp(3) NOT NULL , +"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id")) + +CREATE TABLE "public"."Baseline" ( +"id" text NOT NULL , +"baselineName" text NOT NULL , +"testVariationId" text NOT NULL , +"testRunId" text , +"updatedAt" timestamp(3) NOT NULL , +"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id")) + +CREATE TABLE "public"."User" ( +"id" text NOT NULL , +"email" text NOT NULL , +"password" text NOT NULL , +"firstName" text , +"lastName" text , +"apiKey" text NOT NULL , +"isActive" boolean NOT NULL DEFAULT true, +"updatedAt" timestamp(3) NOT NULL , +"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id")) + +CREATE UNIQUE INDEX "Project.name" ON "public"."Project"("name") + +CREATE UNIQUE INDEX "unique variation" ON "public"."TestVariation"("name","browser","device","os","viewport","branchName") + +CREATE UNIQUE INDEX "Baseline_testRunId" ON "public"."Baseline"("testRunId") + +CREATE UNIQUE INDEX "User.email" ON "public"."User"("email") + +CREATE UNIQUE INDEX "User.apiKey" ON "public"."User"("apiKey") + +ALTER TABLE "public"."Build" ADD FOREIGN KEY ("projectId")REFERENCES "public"."Project"("id") ON DELETE CASCADE ON UPDATE CASCADE + +ALTER TABLE "public"."Build" ADD FOREIGN KEY ("userId")REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE + +ALTER TABLE "public"."TestRun" ADD FOREIGN KEY ("buildId")REFERENCES "public"."Build"("id") ON DELETE CASCADE ON UPDATE CASCADE + +ALTER TABLE "public"."TestRun" ADD FOREIGN KEY ("testVariationId")REFERENCES "public"."TestVariation"("id") ON DELETE CASCADE ON UPDATE CASCADE + +ALTER TABLE "public"."TestVariation" ADD FOREIGN KEY ("projectId")REFERENCES "public"."Project"("id") ON DELETE CASCADE ON UPDATE CASCADE + +ALTER TABLE "public"."Baseline" ADD FOREIGN KEY ("testVariationId")REFERENCES "public"."TestVariation"("id") ON DELETE CASCADE ON UPDATE CASCADE + +ALTER TABLE "public"."Baseline" ADD FOREIGN KEY ("testRunId")REFERENCES "public"."TestRun"("id") ON DELETE SET NULL ON UPDATE CASCADE +``` + +## Changes + +```diff +diff --git schema.prisma schema.prisma +migration 20200707182652-project-name-unique-constraint..20200713222147-branch-strategy +--- datamodel.dml ++++ datamodel.dml +@@ -3,9 +3,9 @@ + } + datasource db { + provider = "postgresql" +- url = "***" ++ url = "***" + } + model Build { + id String @default(uuid()) @id +@@ -23,8 +23,9 @@ + model Project { + id String @default(uuid()) @id + name String ++ mainBranchName String @default("master") + builds Build[] + testVariations TestVariation[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +@@ -43,8 +44,9 @@ + buildId String + build Build @relation(fields: [buildId], references: [id]) + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) ++ merge Boolean @default(false) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + // Test variation data + name String @default("") +@@ -55,13 +57,16 @@ + baselineName String? + ignoreAreas String @default("[]") + comment String? + baseline Baseline? ++ branchName String @default("master") ++ baselineBranchName String? + } + model TestVariation { + id String @default(uuid()) @id + name String ++ branchName String @default("master") + browser String? + device String? + os String? + viewport String? +@@ -73,8 +78,10 @@ + baselines Baseline[] + comment String? + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) ++ ++ @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") + } + model Baseline { + id String @default(uuid()) @id +``` + + diff --git a/prisma/migrations/20200713221657-branch-strategy/schema.prisma b/prisma/migrations/20200713222147-branch-strategy/schema.prisma similarity index 97% rename from prisma/migrations/20200713221657-branch-strategy/schema.prisma rename to prisma/migrations/20200713222147-branch-strategy/schema.prisma index c76b8f51..ca578199 100644 --- a/prisma/migrations/20200713221657-branch-strategy/schema.prisma +++ b/prisma/migrations/20200713222147-branch-strategy/schema.prisma @@ -80,7 +80,7 @@ model TestVariation { updatedAt DateTime @updatedAt createdAt DateTime @default(now()) - // @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") + @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") } model Baseline { diff --git a/prisma/migrations/20200713221657-branch-strategy/steps.json b/prisma/migrations/20200713222147-branch-strategy/steps.json similarity index 86% rename from prisma/migrations/20200713221657-branch-strategy/steps.json rename to prisma/migrations/20200713222147-branch-strategy/steps.json index 3fa55bc9..a5355db8 100644 --- a/prisma/migrations/20200713221657-branch-strategy/steps.json +++ b/prisma/migrations/20200713222147-branch-strategy/steps.json @@ -135,6 +135,26 @@ }, "argument": "", "value": "\"master\"" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Model", + "model": "TestVariation", + "arguments": [ + { + "name": "", + "value": "[name, browser, device, os, viewport, branchName]" + }, + { + "name": "name", + "value": "\"unique variation\"" + } + ] + }, + "directive": "unique" + } } ] } \ No newline at end of file diff --git a/prisma/migrations/migrate.lock b/prisma/migrations/migrate.lock index 97113db4..7dc831c5 100644 --- a/prisma/migrations/migrate.lock +++ b/prisma/migrations/migrate.lock @@ -5,4 +5,4 @@ 20200526195312-approved-test-status-added 20200627134248-comment-added 20200707182652-project-name-unique-constraint -20200713221657-branch-strategy \ No newline at end of file +20200713222147-branch-strategy \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 276c92e1..ce6b6641 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -80,7 +80,7 @@ model TestVariation { updatedAt DateTime @updatedAt createdAt DateTime @default(now()) - // @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") + @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") } model Baseline { From 55667b278775eba9f2f8cfabcc43948fad564ccf Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Mon, 13 Jul 2020 22:54:56 +0200 Subject: [PATCH 03/16] build create updated --- src/builds/builds.service.ts | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/builds/builds.service.ts b/src/builds/builds.service.ts index f5b0508e..2cc78060 100644 --- a/src/builds/builds.service.ts +++ b/src/builds/builds.service.ts @@ -1,10 +1,11 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { CreateBuildDto } from './dto/build-create.dto'; import { PrismaService } from '../prisma/prisma.service'; -import { Build } from '@prisma/client'; +import { Build, Project } from '@prisma/client'; import { TestRunsService } from '../test-runs/test-runs.service'; import { EventsGateway } from '../events/events.gateway'; import { BuildDto } from './dto/build.dto'; +import uuidAPIKey from 'uuid-apikey'; @Injectable() export class BuildsService { @@ -27,13 +28,21 @@ export class BuildsService { } async create(createBuildDto: CreateBuildDto): Promise { - const projects = await this.prismaService.project.findMany({ - where: { - OR: [{ id: createBuildDto.project }, { name: createBuildDto.project }], - }, - }); + let project: Project; + + if (uuidAPIKey.isUUID(createBuildDto.project)) { + project = await this.prismaService.project.findOne({ + where: { id: createBuildDto.project }, + }); + } else { + project = await this.prismaService.project.findOne({ + where: { + name: createBuildDto.project, + }, + }); + } - if (projects.length <= 0) { + if (!project) { throw new HttpException(`Project not found`, HttpStatus.NOT_FOUND); } @@ -42,7 +51,7 @@ export class BuildsService { branchName: createBuildDto.branchName, project: { connect: { - id: projects[0].id, + id: project.id, }, }, }, From 62be045208aa2dfb892aa3d200ec787a94fa3c1f Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Mon, 13 Jul 2020 23:04:21 +0200 Subject: [PATCH 04/16] approveTestRun bool pipe used --- src/test-runs/test-runs.controller.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/test-runs/test-runs.controller.ts b/src/test-runs/test-runs.controller.ts index 4350c6a5..a1068ca9 100644 --- a/src/test-runs/test-runs.controller.ts +++ b/src/test-runs/test-runs.controller.ts @@ -1,4 +1,16 @@ -import { Controller, Delete, UseGuards, Param, ParseUUIDPipe, Put, Body, Get, Query, Post } from '@nestjs/common'; +import { + Controller, + Delete, + UseGuards, + Param, + ParseUUIDPipe, + Put, + Body, + Get, + Query, + Post, + ParseBoolPipe, +} from '@nestjs/common'; import { ApiTags, ApiParam, ApiBearerAuth, ApiQuery, ApiSecurity, ApiOkResponse } from '@nestjs/swagger'; import { JwtAuthGuard } from '../auth/guards/auth.guard'; import { TestRun } from '@prisma/client'; @@ -35,7 +47,10 @@ export class TestRunsController { @ApiQuery({ name: 'merge', required: false }) @ApiBearerAuth() @UseGuards(JwtAuthGuard) - approveTestRun(@Query('id', new ParseUUIDPipe()) id: string, @Query('merge') merge: boolean): Promise { + approveTestRun( + @Query('id', new ParseUUIDPipe()) id: string, + @Query('merge', new ParseBoolPipe()) merge: boolean + ): Promise { return this.testRunsService.approve(id, merge); } From 4abbe7bb2a8b6447596f7511a51678cb8d5a85d0 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Mon, 13 Jul 2020 23:07:53 +0200 Subject: [PATCH 05/16] Update build.dto.spec.ts --- src/builds/dto/build.dto.spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/builds/dto/build.dto.spec.ts b/src/builds/dto/build.dto.spec.ts index ae366886..5db0ff22 100644 --- a/src/builds/dto/build.dto.spec.ts +++ b/src/builds/dto/build.dto.spec.ts @@ -87,6 +87,7 @@ describe('BuildDto', () => { comment: 'some comment1', branchName: 'develop', baselineBranchName: 'master', + merge: true, }, { id: '10fb5e02-64e0-4cf5-9f17-c00ab3c96658', @@ -110,6 +111,7 @@ describe('BuildDto', () => { comment: 'some comment2', branchName: 'develop', baselineBranchName: 'master', + merge: false }, ], }; @@ -165,6 +167,7 @@ describe('BuildDto', () => { comment: 'some comment', branchName: 'develop', baselineBranchName: 'master', + merge: false }, { id: '10fb5e02-64e0-4cf5-9f17-c00ab3c96658', @@ -188,6 +191,7 @@ describe('BuildDto', () => { comment: 'some comment1', branchName: 'develop', baselineBranchName: 'master', + merge: false }, { id: '10fb5e02-64e0-4cf5-9f17-c00ab3c96658', @@ -211,6 +215,7 @@ describe('BuildDto', () => { comment: 'some comment2', branchName: 'develop', baselineBranchName: 'master', + merge: false }, ], }; @@ -266,6 +271,7 @@ describe('BuildDto', () => { comment: 'some comment1', branchName: 'develop', baselineBranchName: 'master', + merge: false }, { id: '10fb5e02-64e0-4cf5-9f17-c00ab3c96658', @@ -289,6 +295,7 @@ describe('BuildDto', () => { comment: 'some comment2', branchName: 'develop', baselineBranchName: 'master', + merge: false }, { id: '10fb5e02-64e0-4cf5-9f17-c00ab3c96658', @@ -312,6 +319,7 @@ describe('BuildDto', () => { comment: null, branchName: 'develop', baselineBranchName: 'master', + merge: false }, { id: '10fb5e02-64e0-4cf5-9f17-c00ab3c96658', @@ -335,6 +343,7 @@ describe('BuildDto', () => { comment: 'some comment', branchName: 'develop', baselineBranchName: 'master', + merge: false }, { id: '10fb5e02-64e0-4cf5-9f17-c00ab3c96658', @@ -358,6 +367,7 @@ describe('BuildDto', () => { comment: 'some comment', branchName: 'develop', baselineBranchName: 'master', + merge: false }, ], }; From a7ec5b1d9d7b3836cbe7172de17d268e8ac0ea01 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Mon, 13 Jul 2020 23:21:13 +0200 Subject: [PATCH 06/16] Update builds.service.spec.ts --- src/builds/builds.service.spec.ts | 71 ++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/src/builds/builds.service.spec.ts b/src/builds/builds.service.spec.ts index 95d24c8f..56ea653f 100644 --- a/src/builds/builds.service.spec.ts +++ b/src/builds/builds.service.spec.ts @@ -18,7 +18,7 @@ const initService = async ({ buildDeleteMock = jest.fn(), testRunDeleteMock = jest.fn(), eventsBuildCreatedMock = jest.fn(), - projectFindManyMock = jest.fn(), + projectFindOneMock = jest.fn(), }) => { const module: TestingModule = await Test.createTestingModule({ providers: [ @@ -27,7 +27,7 @@ const initService = async ({ provide: PrismaService, useValue: { project: { - findMany: projectFindManyMock, + findOne: projectFindOneMock, }, build: { findMany: buildFindManyMock, @@ -91,7 +91,8 @@ describe('BuildsService', () => { ignoreAreas: '[]', comment: 'some comment', branchName: 'develop', - baselineBranchName: 'master' + baselineBranchName: 'master', + merge: false, }, ], }; @@ -127,12 +128,12 @@ describe('BuildsService', () => { }); describe('create', () => { - const createBuildDto: CreateBuildDto = { - branchName: 'branchName', - project: 'project id or name', - }; + it('should create by name', async () => { + const createBuildDto: CreateBuildDto = { + branchName: 'branchName', + project: 'name', + }; - it('should create', async () => { const project: Project = { id: 'project id', name: 'name', @@ -141,18 +142,54 @@ describe('BuildsService', () => { createdAt: new Date(), }; const buildCreateMock = jest.fn().mockResolvedValueOnce(build); - const projectFindManyMock = jest.fn().mockResolvedValueOnce([project]); + const projectFindOneMock = jest.fn().mockResolvedValueOnce(project); const eventsBuildCreatedMock = jest.fn(); mocked(BuildDto).mockReturnValueOnce(buildDto); - service = await initService({ buildCreateMock, eventsBuildCreatedMock, projectFindManyMock }); + service = await initService({ buildCreateMock, eventsBuildCreatedMock, projectFindOneMock }); const result = await service.create(createBuildDto); - expect(projectFindManyMock).toHaveBeenCalledWith({ - where: { - OR: [{ id: createBuildDto.project }, { name: createBuildDto.project }], + expect(projectFindOneMock).toHaveBeenCalledWith({ + where: { name: createBuildDto.project }, + }); + expect(buildCreateMock).toHaveBeenCalledWith({ + data: { + branchName: createBuildDto.branchName, + project: { + connect: { + id: project.id, + }, + }, }, }); + expect(eventsBuildCreatedMock).toHaveBeenCalledWith(buildDto); + expect(result).toBe(buildDto); + }); + + it('should create by UUID', async () => { + const createBuildDto: CreateBuildDto = { + branchName: 'branchName', + project: '6bdd3704-90af-4b1b-94cb-f183e500f5cb', + }; + + const project: Project = { + id: '6bdd3704-90af-4b1b-94cb-f183e500f5cb', + name: 'name', + mainBranchName: 'master', + updatedAt: new Date(), + createdAt: new Date(), + }; + const buildCreateMock = jest.fn().mockResolvedValueOnce(build); + const projectFindOneMock = jest.fn().mockResolvedValueOnce(project); + const eventsBuildCreatedMock = jest.fn(); + mocked(BuildDto).mockReturnValueOnce(buildDto); + service = await initService({ buildCreateMock, eventsBuildCreatedMock, projectFindOneMock }); + + const result = await service.create(createBuildDto); + + expect(projectFindOneMock).toHaveBeenCalledWith({ + where: { id: createBuildDto.project }, + }); expect(buildCreateMock).toHaveBeenCalledWith({ data: { branchName: createBuildDto.branchName, @@ -168,8 +205,12 @@ describe('BuildsService', () => { }); it('should throw exception if not found', async () => { - const projectFindManyMock = jest.fn().mockResolvedValueOnce([]); - service = await initService({ projectFindManyMock }); + const createBuildDto: CreateBuildDto = { + branchName: 'branchName', + project: 'nonexisting', + }; + const projectFindOneMock = jest.fn().mockResolvedValueOnce(undefined); + service = await initService({ projectFindOneMock }); await expect(service.create(createBuildDto)).rejects.toThrowError( new HttpException('Project not found', HttpStatus.NOT_FOUND) From 95144702608259adf0060d2dce713565ab2760c7 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Mon, 13 Jul 2020 23:32:16 +0200 Subject: [PATCH 07/16] Update test-runs.service.spec.ts --- src/test-runs/test-runs.service.spec.ts | 82 ++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/src/test-runs/test-runs.service.spec.ts b/src/test-runs/test-runs.service.spec.ts index 08535c9d..e69cbd4c 100644 --- a/src/test-runs/test-runs.service.spec.ts +++ b/src/test-runs/test-runs.service.spec.ts @@ -159,6 +159,7 @@ describe('TestRunsService', () => { branchName: 'master', baselineBranchName: 'master', comment: 'some comment', + merge: false }; const testRunUpdateMock = jest.fn(); const testRunFindOneMock = jest.fn().mockResolvedValueOnce(testRun); @@ -178,7 +179,79 @@ describe('TestRunsService', () => { service.findOne = testRunFindOneMock; service.emitUpdateBuildEvent = jest.fn(); - await service.approve(testRun.id); + await service.approve(testRun.id, false); + + expect(testRunFindOneMock).toHaveBeenCalledWith(testRun.id); + expect(getImageMock).toHaveBeenCalledWith(testRun.imageName); + expect(saveImageMock).toHaveBeenCalledTimes(1); + expect(testRunUpdateMock).toHaveBeenCalledWith({ + where: { id: testRun.id }, + data: { + status: TestStatus.approved, + testVariation: { + update: { + baselineName, + baselines: { + create: { + baselineName, + testRun: { + connect: { + id: testRun.id, + }, + }, + }, + }, + }, + }, + }, + }); + expect(service.emitUpdateBuildEvent).toBeCalledWith(testRun.buildId); + }); + + it('should approve merge', async () => { + const testRun: TestRun = { + id: 'id', + imageName: 'imageName', + diffName: 'diffName', + baselineName: 'baselineName', + diffPercent: 1, + pixelMisMatchCount: 10, + diffTollerancePercent: 12, + status: TestStatus.new, + buildId: 'buildId', + testVariationId: 'testVariationId', + updatedAt: new Date(), + createdAt: new Date(), + name: 'test run name', + ignoreAreas: '[]', + browser: 'browser', + device: 'device', + os: 'os', + viewport: 'viewport', + branchName: 'develop', + baselineBranchName: 'master', + comment: 'some comment', + merge: false + }; + const testRunUpdateMock = jest.fn(); + const testRunFindOneMock = jest.fn().mockResolvedValueOnce(testRun); + const baselineName = 'some baseline name'; + const saveImageMock = jest.fn().mockReturnValueOnce(baselineName); + const getImageMock = jest.fn().mockReturnValueOnce( + new PNG({ + width: 10, + height: 10, + }) + ); + service = await initService({ + testRunUpdateMock, + saveImageMock, + getImageMock, + }); + service.findOne = testRunFindOneMock; + service.emitUpdateBuildEvent = jest.fn(); + + await service.approve(testRun.id, true); expect(testRunFindOneMock).toHaveBeenCalledWith(testRun.id); expect(getImageMock).toHaveBeenCalledWith(testRun.imageName); @@ -232,6 +305,7 @@ describe('TestRunsService', () => { branchName: 'develop', baselineBranchName: 'master', comment: 'some comment', + merge: false, testVariation: { id: '123', projectId: 'project Id', @@ -285,7 +359,7 @@ describe('TestRunsService', () => { service.findOne = testRunFindOneMock; service.emitUpdateBuildEvent = jest.fn(); - await service.approve(testRun.id); + await service.approve(testRun.id, false); expect(testRunFindOneMock).toHaveBeenCalledWith(testRun.id); expect(getImageMock).toHaveBeenCalledWith(testRun.imageName); @@ -342,6 +416,7 @@ describe('TestRunsService', () => { device: 'device', diffTollerancePercent: undefined, branchName: 'develop', + merge: true, }; const testRunWithResult = { id: 'id', @@ -435,6 +510,7 @@ describe('TestRunsService', () => { baselineBranchName: testVariation.branchName, branchName: createTestRequestDto.branchName, diffTollerancePercent: createTestRequestDto.diffTollerancePercent, + merge: createTestRequestDto.merge, status: TestStatus.new, }, }); @@ -773,6 +849,7 @@ describe('TestRunsService', () => { comment: 'some comment', baselineBranchName: 'master', branchName: 'develop', + merge: false, }, ], }; @@ -845,6 +922,7 @@ describe('TestRunsService', () => { comment: 'some comment', baselineBranchName: 'master', branchName: 'develop', + merge: false, }; const testVariationFindOrCreateMock = jest.fn().mockResolvedValueOnce(testVariation); const testRunFindManyMock = jest.fn().mockResolvedValueOnce([testRun]); From 9497e9d12133e90d4f01792f502a1eee28b3fc16 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Tue, 14 Jul 2020 20:28:13 +0200 Subject: [PATCH 08/16] prisma updated 2.2.2 --- package-lock.json | 12 ++++++------ package.json | 4 ++-- prisma/package-lock.json | 12 ++++++------ prisma/package.json | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index c53030a5..ab1cf5a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1333,15 +1333,15 @@ } }, "@prisma/cli": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.2.1.tgz", - "integrity": "sha512-1pAk1KkV+I9oOzgMS/eWFsz3r/RPckDhCez5NV96zxHV/tzBCHmnfhIv1rD6Q12nj7fSjWlYUqAwprVcWCVtTg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.2.2.tgz", + "integrity": "sha512-pk18QyfTHsAGctqoBu4pd9k/pAJ+uaD5GfrAE1gZkGuhmUGjh2hXmgJEKeQK+n1J4fvcstmDi38oHwskYyBv9w==", "dev": true }, "@prisma/client": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.2.1.tgz", - "integrity": "sha512-DDZwvO1VxcztIiCzr4S3K0YeAv4+or+Q9ZEjkUgRtrhxBs3/VEBSYh4+0PhMZ/0HZx4QXYZzxzp6tg+54PGpAQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.2.2.tgz", + "integrity": "sha512-//Ji25pubuXCJDcXaE7Es/ytRB7ojdj8H2YCIkziQVWY62Nl/j9vXYM7WzA4z1lysOlbKFlH9e0W1sy6LQpUpg==", "requires": { "pkg-up": "^3.1.0" } diff --git a/package.json b/package.json index 84742ecb..e0367785 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@nestjs/platform-socket.io": "^7.1.3", "@nestjs/swagger": "^4.5.1", "@nestjs/websockets": "^7.1.3", - "@prisma/client": "^2.2.1", + "@prisma/client": "^2.2.2", "bcryptjs": "^2.4.3", "class-transformer": "^0.2.3", "class-validator": "^0.11.1", @@ -52,7 +52,7 @@ "@nestjs/cli": "^7.0.0", "@nestjs/schematics": "^7.0.0", "@nestjs/testing": "^7.0.0", - "@prisma/cli": "^2.2.1", + "@prisma/cli": "^2.2.2", "@types/bcryptjs": "^2.4.2", "@types/express": "^4.17.3", "@types/jest": "25.1.4", diff --git a/prisma/package-lock.json b/prisma/package-lock.json index cf8a3ef1..c1dc5044 100644 --- a/prisma/package-lock.json +++ b/prisma/package-lock.json @@ -5,15 +5,15 @@ "requires": true, "dependencies": { "@prisma/cli": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.2.1.tgz", - "integrity": "sha512-1pAk1KkV+I9oOzgMS/eWFsz3r/RPckDhCez5NV96zxHV/tzBCHmnfhIv1rD6Q12nj7fSjWlYUqAwprVcWCVtTg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.2.2.tgz", + "integrity": "sha512-pk18QyfTHsAGctqoBu4pd9k/pAJ+uaD5GfrAE1gZkGuhmUGjh2hXmgJEKeQK+n1J4fvcstmDi38oHwskYyBv9w==", "dev": true }, "@prisma/client": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.2.1.tgz", - "integrity": "sha512-DDZwvO1VxcztIiCzr4S3K0YeAv4+or+Q9ZEjkUgRtrhxBs3/VEBSYh4+0PhMZ/0HZx4QXYZzxzp6tg+54PGpAQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.2.2.tgz", + "integrity": "sha512-//Ji25pubuXCJDcXaE7Es/ytRB7ojdj8H2YCIkziQVWY62Nl/j9vXYM7WzA4z1lysOlbKFlH9e0W1sy6LQpUpg==", "requires": { "pkg-up": "^3.1.0" } diff --git a/prisma/package.json b/prisma/package.json index 4780b8f3..a61afa96 100644 --- a/prisma/package.json +++ b/prisma/package.json @@ -7,12 +7,12 @@ "license": "UNLICENSED", "scripts": {}, "dependencies": { - "@prisma/client": "^2.2.1", + "@prisma/client": "^2.2.2", "bcryptjs": "^2.4.3", "uuid-apikey": "^1.4.6" }, "devDependencies": { - "@prisma/cli": "^2.2.1", + "@prisma/cli": "^2.2.2", "@types/bcryptjs": "^2.4.2", "@types/uuid-apikey": "^1.4.0", "ts-node": "^8.8.2", From 353170a11b1099beb35a3a95219305dd9a723bbe Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Tue, 14 Jul 2020 21:20:21 +0200 Subject: [PATCH 09/16] merge covered with tests --- src/test-runs/test-runs.service.spec.ts | 139 +++++++++++++++++++++++- src/test-runs/test-runs.service.ts | 42 +++---- 2 files changed, 159 insertions(+), 22 deletions(-) diff --git a/src/test-runs/test-runs.service.spec.ts b/src/test-runs/test-runs.service.spec.ts index e69cbd4c..b6cb7c97 100644 --- a/src/test-runs/test-runs.service.spec.ts +++ b/src/test-runs/test-runs.service.spec.ts @@ -4,7 +4,7 @@ import { TestRunsService } from './test-runs.service'; import { PrismaService } from '../prisma/prisma.service'; import { StaticService } from '../shared/static/static.service'; import { PNG } from 'pngjs'; -import { TestStatus, Build, TestRun, TestVariation } from '@prisma/client'; +import { TestStatus, Build, TestRun, TestVariation, Project } from '@prisma/client'; import Pixelmatch from 'pixelmatch'; import { CreateTestRequestDto } from './dto/create-test-request.dto'; import { TestRunResultDto } from './dto/testRunResult.dto'; @@ -30,10 +30,14 @@ const initService = async ({ deleteImageMock = jest.fn(), eventNewTestRunMock = jest.fn(), eventBuildUpdatedMock = jest.fn(), + eventBuildCreatedMock = jest.fn(), buildFindOneMock = jest.fn(), + buildCreateMock = jest.fn(), testVariationCreateMock = jest.fn(), + testVariationFindManyMock = jest.fn(), baselineCreateMock = jest.fn(), testVariationFindOrCreateMock = jest.fn(), + projectFindOneMock = jest.fn(), }) => { const module: TestingModule = await Test.createTestingModule({ providers: [ @@ -50,13 +54,18 @@ const initService = async ({ }, build: { findOne: buildFindOneMock, + create: buildCreateMock, }, testVariation: { create: testVariationCreateMock, + findMany: testVariationFindManyMock, }, baseline: { create: baselineCreateMock, }, + project: { + findOne: projectFindOneMock, + }, }, }, { @@ -72,6 +81,7 @@ const initService = async ({ useValue: { newTestRun: eventNewTestRunMock, buildUpdated: eventBuildUpdatedMock, + buildCreated: eventBuildCreatedMock, }, }, { @@ -159,7 +169,7 @@ describe('TestRunsService', () => { branchName: 'master', baselineBranchName: 'master', comment: 'some comment', - merge: false + merge: false, }; const testRunUpdateMock = jest.fn(); const testRunFindOneMock = jest.fn().mockResolvedValueOnce(testRun); @@ -231,7 +241,7 @@ describe('TestRunsService', () => { branchName: 'develop', baselineBranchName: 'master', comment: 'some comment', - merge: false + merge: false, }; const testRunUpdateMock = jest.fn(); const testRunFindOneMock = jest.fn().mockResolvedValueOnce(testRun); @@ -949,4 +959,127 @@ describe('TestRunsService', () => { expect(createMock).toHaveBeenCalledWith(testVariation, createTestRequestDto); expect(mocked(TestRunResultDto)).toHaveBeenCalledWith(testRun, testVariation); }); + + it('merge', async () => { + const mergedBranch = 'develop'; + const project: Project = { + id: 'some id', + name: 'some name', + mainBranchName: 'master', + updatedAt: new Date(), + createdAt: new Date(), + }; + const build: Build = { + id: 'a9385fc1-884d-4f9f-915e-40da0e7773d5', + number: null, + branchName: project.mainBranchName, + status: null, + projectId: project.id, + updatedAt: new Date(), + createdAt: new Date(), + userId: null, + }; + const testVariation: TestVariation = { + id: '123', + projectId: project.id, + name: 'Test name', + baselineName: 'baselineName', + os: 'OS', + browser: 'browser', + viewport: 'viewport', + device: 'device', + ignoreAreas: '[]', + comment: 'some comment', + createdAt: new Date(), + updatedAt: new Date(), + branchName: mergedBranch, + }; + const testVariationNoBaseline: TestVariation = { + id: '123', + projectId: project.id, + name: 'Test name', + baselineName: null, + os: 'OS', + browser: 'browser', + viewport: 'viewport', + device: 'device', + ignoreAreas: '[]', + comment: 'some comment', + createdAt: new Date(), + updatedAt: new Date(), + branchName: mergedBranch, + }; + const testVariationMainBranch: TestVariation = { + id: '123', + projectId: project.id, + name: 'Test name', + baselineName: 'baselineName', + os: 'OS', + browser: 'browser', + viewport: 'viewport', + device: 'device', + ignoreAreas: '[]', + comment: 'some comment', + createdAt: new Date(), + updatedAt: new Date(), + branchName: project.mainBranchName, + }; + const projectFindOneMock = jest.fn().mockResolvedValueOnce(project); + const buildCreateMock = jest.fn().mockResolvedValueOnce(build); + const eventBuildCreatedMock = jest.fn(); + const testVariationFindManyMock = jest.fn().mockResolvedValueOnce([testVariation, testVariationNoBaseline]); + const image = new PNG({ + width: 10, + height: 10, + }); + const getImageMock = jest + .fn() + .mockReturnValueOnce(image) + .mockReturnValueOnce(null); + const testVariationFindOrCreateMock = jest.fn().mockResolvedValueOnce(testVariationMainBranch); + const createMock = jest.fn(); + const service = await initService({ + projectFindOneMock, + buildCreateMock, + eventBuildCreatedMock, + testVariationFindManyMock, + testVariationFindOrCreateMock, + getImageMock, + }); + service.create = createMock; + + await service.merge(project.id, mergedBranch); + + expect(projectFindOneMock).toHaveBeenCalledWith({ where: { id: project.id } }); + expect(buildCreateMock).toHaveBeenCalledWith({ + data: { + branchName: project.mainBranchName, + project: { + connect: { + id: project.id, + }, + }, + }, + }); + expect(eventBuildCreatedMock).toHaveBeenCalledWith(new BuildDto(build)); + expect(testVariationFindManyMock).toHaveBeenCalledWith({ + where: { projectId: project.id, branchName: mergedBranch }, + }); + expect(getImageMock).toHaveBeenCalledWith(testVariation.baselineName); + expect(testVariationFindOrCreateMock).toHaveBeenCalledWith(project.id, { + name: testVariation.name, + os: testVariation.os, + device: testVariation.device, + browser: testVariation.browser, + viewport: testVariation.viewport, + branchName: project.mainBranchName, + }); + expect(createMock).toHaveBeenCalledWith(testVariationMainBranch, { + ...testVariation, + buildId: build.id, + imageBase64: PNG.sync.write(image).toString('base64'), + diffTollerancePercent: 0, + merge: true, + }); + }); }); diff --git a/src/test-runs/test-runs.service.ts b/src/test-runs/test-runs.service.ts index 688c030e..f733a670 100644 --- a/src/test-runs/test-runs.service.ts +++ b/src/test-runs/test-runs.service.ts @@ -70,25 +70,29 @@ export class TestRunsService { testVariations.map(async sideBranchTestVariation => { const baseline = this.staticService.getImage(sideBranchTestVariation.baselineName); if (baseline) { - // get main branch variation - const baselineData = convertBaselineDataToQuery({ - ...sideBranchTestVariation, - branchName: project.mainBranchName, - }); - const mainBranchTestVariation = await this.testVariationService.findOrCreate(projectId, baselineData); - - // get side branch request - const imageBase64 = PNG.sync.write(baseline).toString('base64'); - const createTestRequestDto: CreateTestRequestDto = { - ...sideBranchTestVariation, - projectId, - buildId: build.id, - imageBase64, - diffTollerancePercent: 0, - merge: true, - }; - - return this.create(mainBranchTestVariation, createTestRequestDto); + try { + let imageBase64 = PNG.sync.write(baseline).toString('base64'); + + // get main branch variation + const baselineData = convertBaselineDataToQuery({ + ...sideBranchTestVariation, + branchName: project.mainBranchName, + }); + const mainBranchTestVariation = await this.testVariationService.findOrCreate(projectId, baselineData); + + // get side branch request + const createTestRequestDto: CreateTestRequestDto = { + ...sideBranchTestVariation, + buildId: build.id, + imageBase64, + diffTollerancePercent: 0, + merge: true, + }; + + return this.create(mainBranchTestVariation, createTestRequestDto); + } catch (err) { + console.log(err); + } } }) ); From 3a04fd615e2fd7fe99d9ec720ed1cb79f1cb0bb1 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Thu, 16 Jul 2020 22:12:47 +0200 Subject: [PATCH 10/16] migration updated --- .../20200713222147-branch-strategy/README.md | 180 ------------------ .../20200715232608-branch-strategy/README.md | 75 ++++++++ .../schema.prisma | 2 +- .../steps.json | 4 - prisma/migrations/migrate.lock | 2 +- prisma/schema.prisma | 2 +- 6 files changed, 78 insertions(+), 187 deletions(-) delete mode 100644 prisma/migrations/20200713222147-branch-strategy/README.md create mode 100644 prisma/migrations/20200715232608-branch-strategy/README.md rename prisma/migrations/{20200713222147-branch-strategy => 20200715232608-branch-strategy}/schema.prisma (97%) rename prisma/migrations/{20200713222147-branch-strategy => 20200715232608-branch-strategy}/steps.json (96%) diff --git a/prisma/migrations/20200713222147-branch-strategy/README.md b/prisma/migrations/20200713222147-branch-strategy/README.md deleted file mode 100644 index 71d95908..00000000 --- a/prisma/migrations/20200713222147-branch-strategy/README.md +++ /dev/null @@ -1,180 +0,0 @@ -# Migration `20200713222147-branch-strategy` - -This migration has been generated by Pavel Strunkin at 7/13/2020, 10:21:47 PM. -You can check out the [state of the schema](./schema.prisma) after the migration. - -## Database Steps - -```sql -CREATE TYPE "TestStatus" AS ENUM ('failed', 'new', 'ok', 'unresolved', 'approved'); - -CREATE TABLE "public"."Build" ( -"id" text NOT NULL , -"number" integer , -"branchName" text , -"status" text , -"projectId" text NOT NULL , -"updatedAt" timestamp(3) NOT NULL , -"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, -"userId" text , - PRIMARY KEY ("id")) - -CREATE TABLE "public"."Project" ( -"id" text NOT NULL , -"name" text NOT NULL , -"mainBranchName" text NOT NULL DEFAULT E'master', -"updatedAt" timestamp(3) NOT NULL , -"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id")) - -CREATE TABLE "public"."TestRun" ( -"id" text NOT NULL , -"imageName" text NOT NULL , -"diffName" text , -"diffPercent" Decimal(65,30) , -"diffTollerancePercent" Decimal(65,30) NOT NULL DEFAULT 1.0, -"pixelMisMatchCount" integer , -"status" "TestStatus" NOT NULL , -"buildId" text NOT NULL , -"testVariationId" text NOT NULL , -"merge" boolean NOT NULL DEFAULT false, -"updatedAt" timestamp(3) NOT NULL , -"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, -"name" text NOT NULL DEFAULT E'', -"browser" text , -"device" text , -"os" text , -"viewport" text , -"baselineName" text , -"ignoreAreas" text NOT NULL DEFAULT E'[]', -"comment" text , -"branchName" text NOT NULL DEFAULT E'master', -"baselineBranchName" text , - PRIMARY KEY ("id")) - -CREATE TABLE "public"."TestVariation" ( -"id" text NOT NULL , -"name" text NOT NULL , -"branchName" text NOT NULL DEFAULT E'master', -"browser" text , -"device" text , -"os" text , -"viewport" text , -"baselineName" text , -"ignoreAreas" text NOT NULL DEFAULT E'[]', -"projectId" text NOT NULL , -"comment" text , -"updatedAt" timestamp(3) NOT NULL , -"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id")) - -CREATE TABLE "public"."Baseline" ( -"id" text NOT NULL , -"baselineName" text NOT NULL , -"testVariationId" text NOT NULL , -"testRunId" text , -"updatedAt" timestamp(3) NOT NULL , -"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id")) - -CREATE TABLE "public"."User" ( -"id" text NOT NULL , -"email" text NOT NULL , -"password" text NOT NULL , -"firstName" text , -"lastName" text , -"apiKey" text NOT NULL , -"isActive" boolean NOT NULL DEFAULT true, -"updatedAt" timestamp(3) NOT NULL , -"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id")) - -CREATE UNIQUE INDEX "Project.name" ON "public"."Project"("name") - -CREATE UNIQUE INDEX "unique variation" ON "public"."TestVariation"("name","browser","device","os","viewport","branchName") - -CREATE UNIQUE INDEX "Baseline_testRunId" ON "public"."Baseline"("testRunId") - -CREATE UNIQUE INDEX "User.email" ON "public"."User"("email") - -CREATE UNIQUE INDEX "User.apiKey" ON "public"."User"("apiKey") - -ALTER TABLE "public"."Build" ADD FOREIGN KEY ("projectId")REFERENCES "public"."Project"("id") ON DELETE CASCADE ON UPDATE CASCADE - -ALTER TABLE "public"."Build" ADD FOREIGN KEY ("userId")REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE - -ALTER TABLE "public"."TestRun" ADD FOREIGN KEY ("buildId")REFERENCES "public"."Build"("id") ON DELETE CASCADE ON UPDATE CASCADE - -ALTER TABLE "public"."TestRun" ADD FOREIGN KEY ("testVariationId")REFERENCES "public"."TestVariation"("id") ON DELETE CASCADE ON UPDATE CASCADE - -ALTER TABLE "public"."TestVariation" ADD FOREIGN KEY ("projectId")REFERENCES "public"."Project"("id") ON DELETE CASCADE ON UPDATE CASCADE - -ALTER TABLE "public"."Baseline" ADD FOREIGN KEY ("testVariationId")REFERENCES "public"."TestVariation"("id") ON DELETE CASCADE ON UPDATE CASCADE - -ALTER TABLE "public"."Baseline" ADD FOREIGN KEY ("testRunId")REFERENCES "public"."TestRun"("id") ON DELETE SET NULL ON UPDATE CASCADE -``` - -## Changes - -```diff -diff --git schema.prisma schema.prisma -migration 20200707182652-project-name-unique-constraint..20200713222147-branch-strategy ---- datamodel.dml -+++ datamodel.dml -@@ -3,9 +3,9 @@ - } - datasource db { - provider = "postgresql" -- url = "***" -+ url = "***" - } - model Build { - id String @default(uuid()) @id -@@ -23,8 +23,9 @@ - model Project { - id String @default(uuid()) @id - name String -+ mainBranchName String @default("master") - builds Build[] - testVariations TestVariation[] - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -@@ -43,8 +44,9 @@ - buildId String - build Build @relation(fields: [buildId], references: [id]) - testVariationId String - testVariation TestVariation @relation(fields: [testVariationId], references: [id]) -+ merge Boolean @default(false) - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) - // Test variation data - name String @default("") -@@ -55,13 +57,16 @@ - baselineName String? - ignoreAreas String @default("[]") - comment String? - baseline Baseline? -+ branchName String @default("master") -+ baselineBranchName String? - } - model TestVariation { - id String @default(uuid()) @id - name String -+ branchName String @default("master") - browser String? - device String? - os String? - viewport String? -@@ -73,8 +78,10 @@ - baselines Baseline[] - comment String? - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -+ -+ @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") - } - model Baseline { - id String @default(uuid()) @id -``` - - diff --git a/prisma/migrations/20200715232608-branch-strategy/README.md b/prisma/migrations/20200715232608-branch-strategy/README.md new file mode 100644 index 00000000..b50ba188 --- /dev/null +++ b/prisma/migrations/20200715232608-branch-strategy/README.md @@ -0,0 +1,75 @@ +# Migration `20200715232608-branch-strategy` + +This migration has been generated by Pavel Strunkin at 7/15/2020, 11:26:08 PM. +You can check out the [state of the schema](./schema.prisma) after the migration. + +## Database Steps + +```sql +CREATE UNIQUE INDEX "TestVariation.name_browser_device_os_viewport_branchName" ON "public"."TestVariation"("name","browser","device","os","viewport","branchName") +``` + +## Changes + +```diff +diff --git schema.prisma schema.prisma +migration 20200707182652-project-name-unique-constraint..20200715232608-branch-strategy +--- datamodel.dml ++++ datamodel.dml +@@ -3,9 +3,9 @@ + } + datasource db { + provider = "postgresql" +- url = "***" ++ url = "***" + } + model Build { + id String @default(uuid()) @id +@@ -23,8 +23,9 @@ + model Project { + id String @default(uuid()) @id + name String ++ mainBranchName String @default("master") + builds Build[] + testVariations TestVariation[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +@@ -43,8 +44,9 @@ + buildId String + build Build @relation(fields: [buildId], references: [id]) + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) ++ merge Boolean @default(false) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + // Test variation data + name String @default("") +@@ -55,13 +57,16 @@ + baselineName String? + ignoreAreas String @default("[]") + comment String? + baseline Baseline? ++ branchName String @default("master") ++ baselineBranchName String? + } + model TestVariation { + id String @default(uuid()) @id + name String ++ branchName String @default("master") + browser String? + device String? + os String? + viewport String? +@@ -73,8 +78,10 @@ + baselines Baseline[] + comment String? + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) ++ ++ @@unique([name, browser, device, os, viewport, branchName]) + } + model Baseline { + id String @default(uuid()) @id +``` + + diff --git a/prisma/migrations/20200713222147-branch-strategy/schema.prisma b/prisma/migrations/20200715232608-branch-strategy/schema.prisma similarity index 97% rename from prisma/migrations/20200713222147-branch-strategy/schema.prisma rename to prisma/migrations/20200715232608-branch-strategy/schema.prisma index ca578199..aaaed347 100644 --- a/prisma/migrations/20200713222147-branch-strategy/schema.prisma +++ b/prisma/migrations/20200715232608-branch-strategy/schema.prisma @@ -80,7 +80,7 @@ model TestVariation { updatedAt DateTime @updatedAt createdAt DateTime @default(now()) - @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") + @@unique([name, browser, device, os, viewport, branchName]) } model Baseline { diff --git a/prisma/migrations/20200713222147-branch-strategy/steps.json b/prisma/migrations/20200715232608-branch-strategy/steps.json similarity index 96% rename from prisma/migrations/20200713222147-branch-strategy/steps.json rename to prisma/migrations/20200715232608-branch-strategy/steps.json index a5355db8..8260e776 100644 --- a/prisma/migrations/20200713222147-branch-strategy/steps.json +++ b/prisma/migrations/20200715232608-branch-strategy/steps.json @@ -146,10 +146,6 @@ { "name": "", "value": "[name, browser, device, os, viewport, branchName]" - }, - { - "name": "name", - "value": "\"unique variation\"" } ] }, diff --git a/prisma/migrations/migrate.lock b/prisma/migrations/migrate.lock index 7dc831c5..1db1b94d 100644 --- a/prisma/migrations/migrate.lock +++ b/prisma/migrations/migrate.lock @@ -5,4 +5,4 @@ 20200526195312-approved-test-status-added 20200627134248-comment-added 20200707182652-project-name-unique-constraint -20200713222147-branch-strategy \ No newline at end of file +20200715232608-branch-strategy \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ce6b6641..9067b5d2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -80,7 +80,7 @@ model TestVariation { updatedAt DateTime @updatedAt createdAt DateTime @default(now()) - @@unique([name, browser, device, os, viewport, branchName], name: "unique variation") + @@unique([name, browser, device, os, viewport, branchName]) } model Baseline { From 89c3a43401411a4b6ea24db455522021170dc73c Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Thu, 16 Jul 2020 22:49:41 +0200 Subject: [PATCH 11/16] merge moved to testVariation controller --- src/test-runs/test-runs.controller.ts | 12 -- src/test-runs/test-runs.service.spec.ts | 123 ---------------- src/test-runs/test-runs.service.ts | 56 +------- .../test-variations.controller.ts | 12 ++ src/test-variations/test-variations.module.ts | 7 +- .../test-variations.service.spec.ts | 135 +++++++++++++++++- .../test-variations.service.ts | 66 ++++++++- 7 files changed, 214 insertions(+), 197 deletions(-) diff --git a/src/test-runs/test-runs.controller.ts b/src/test-runs/test-runs.controller.ts index a1068ca9..f83685d2 100644 --- a/src/test-runs/test-runs.controller.ts +++ b/src/test-runs/test-runs.controller.ts @@ -96,16 +96,4 @@ export class TestRunsController { postTestRun(@Body() createTestRequestDto: CreateTestRequestDto): Promise { return this.testRunsService.postTestRun(createTestRequestDto); } - - @Get('merge') - @ApiQuery({ name: 'projectId', required: true }) - @ApiQuery({ name: 'branchName', required: true }) - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - merge( - @Query('projectId', new ParseUUIDPipe()) projectId: string, - @Query('branchName') branchName: string - ): Promise { - return this.testRunsService.merge(projectId, branchName); - } } diff --git a/src/test-runs/test-runs.service.spec.ts b/src/test-runs/test-runs.service.spec.ts index b6cb7c97..bcead47c 100644 --- a/src/test-runs/test-runs.service.spec.ts +++ b/src/test-runs/test-runs.service.spec.ts @@ -959,127 +959,4 @@ describe('TestRunsService', () => { expect(createMock).toHaveBeenCalledWith(testVariation, createTestRequestDto); expect(mocked(TestRunResultDto)).toHaveBeenCalledWith(testRun, testVariation); }); - - it('merge', async () => { - const mergedBranch = 'develop'; - const project: Project = { - id: 'some id', - name: 'some name', - mainBranchName: 'master', - updatedAt: new Date(), - createdAt: new Date(), - }; - const build: Build = { - id: 'a9385fc1-884d-4f9f-915e-40da0e7773d5', - number: null, - branchName: project.mainBranchName, - status: null, - projectId: project.id, - updatedAt: new Date(), - createdAt: new Date(), - userId: null, - }; - const testVariation: TestVariation = { - id: '123', - projectId: project.id, - name: 'Test name', - baselineName: 'baselineName', - os: 'OS', - browser: 'browser', - viewport: 'viewport', - device: 'device', - ignoreAreas: '[]', - comment: 'some comment', - createdAt: new Date(), - updatedAt: new Date(), - branchName: mergedBranch, - }; - const testVariationNoBaseline: TestVariation = { - id: '123', - projectId: project.id, - name: 'Test name', - baselineName: null, - os: 'OS', - browser: 'browser', - viewport: 'viewport', - device: 'device', - ignoreAreas: '[]', - comment: 'some comment', - createdAt: new Date(), - updatedAt: new Date(), - branchName: mergedBranch, - }; - const testVariationMainBranch: TestVariation = { - id: '123', - projectId: project.id, - name: 'Test name', - baselineName: 'baselineName', - os: 'OS', - browser: 'browser', - viewport: 'viewport', - device: 'device', - ignoreAreas: '[]', - comment: 'some comment', - createdAt: new Date(), - updatedAt: new Date(), - branchName: project.mainBranchName, - }; - const projectFindOneMock = jest.fn().mockResolvedValueOnce(project); - const buildCreateMock = jest.fn().mockResolvedValueOnce(build); - const eventBuildCreatedMock = jest.fn(); - const testVariationFindManyMock = jest.fn().mockResolvedValueOnce([testVariation, testVariationNoBaseline]); - const image = new PNG({ - width: 10, - height: 10, - }); - const getImageMock = jest - .fn() - .mockReturnValueOnce(image) - .mockReturnValueOnce(null); - const testVariationFindOrCreateMock = jest.fn().mockResolvedValueOnce(testVariationMainBranch); - const createMock = jest.fn(); - const service = await initService({ - projectFindOneMock, - buildCreateMock, - eventBuildCreatedMock, - testVariationFindManyMock, - testVariationFindOrCreateMock, - getImageMock, - }); - service.create = createMock; - - await service.merge(project.id, mergedBranch); - - expect(projectFindOneMock).toHaveBeenCalledWith({ where: { id: project.id } }); - expect(buildCreateMock).toHaveBeenCalledWith({ - data: { - branchName: project.mainBranchName, - project: { - connect: { - id: project.id, - }, - }, - }, - }); - expect(eventBuildCreatedMock).toHaveBeenCalledWith(new BuildDto(build)); - expect(testVariationFindManyMock).toHaveBeenCalledWith({ - where: { projectId: project.id, branchName: mergedBranch }, - }); - expect(getImageMock).toHaveBeenCalledWith(testVariation.baselineName); - expect(testVariationFindOrCreateMock).toHaveBeenCalledWith(project.id, { - name: testVariation.name, - os: testVariation.os, - device: testVariation.device, - browser: testVariation.browser, - viewport: testVariation.viewport, - branchName: project.mainBranchName, - }); - expect(createMock).toHaveBeenCalledWith(testVariationMainBranch, { - ...testVariation, - buildId: build.id, - imageBase64: PNG.sync.write(image).toString('base64'), - diffTollerancePercent: 0, - merge: true, - }); - }); }); diff --git a/src/test-runs/test-runs.service.ts b/src/test-runs/test-runs.service.ts index f733a670..67ce648b 100644 --- a/src/test-runs/test-runs.service.ts +++ b/src/test-runs/test-runs.service.ts @@ -5,7 +5,7 @@ import { CreateTestRequestDto } from './dto/create-test-request.dto'; import { IgnoreAreaDto } from './dto/ignore-area.dto'; import { StaticService } from '../shared/static/static.service'; import { PrismaService } from '../prisma/prisma.service'; -import { TestRun, TestStatus, TestVariation, Project, Build } from '@prisma/client'; +import { TestRun, TestStatus, TestVariation } from '@prisma/client'; import { DiffResult } from './diffResult'; import { EventsGateway } from '../events/events.gateway'; import { CommentDto } from '../shared/dto/comment.dto'; @@ -44,60 +44,6 @@ export class TestRunsService { }); } - async merge(projectId: string, branchName: string): Promise { - const project: Project = await this.prismaService.project.findOne({ where: { id: projectId } }); - - // create build - const build: Build = await this.prismaService.build.create({ - data: { - branchName: project.mainBranchName, - project: { - connect: { - id: project.id, - }, - }, - }, - }); - this.eventsGateway.buildCreated(new BuildDto(build)); - - // find side branch variations - const testVariations: TestVariation[] = await this.prismaService.testVariation.findMany({ - where: { projectId, branchName }, - }); - - // compare to main branch variations - await Promise.all( - testVariations.map(async sideBranchTestVariation => { - const baseline = this.staticService.getImage(sideBranchTestVariation.baselineName); - if (baseline) { - try { - let imageBase64 = PNG.sync.write(baseline).toString('base64'); - - // get main branch variation - const baselineData = convertBaselineDataToQuery({ - ...sideBranchTestVariation, - branchName: project.mainBranchName, - }); - const mainBranchTestVariation = await this.testVariationService.findOrCreate(projectId, baselineData); - - // get side branch request - const createTestRequestDto: CreateTestRequestDto = { - ...sideBranchTestVariation, - buildId: build.id, - imageBase64, - diffTollerancePercent: 0, - merge: true, - }; - - return this.create(mainBranchTestVariation, createTestRequestDto); - } catch (err) { - console.log(err); - } - } - }) - ); - } - async postTestRun(createTestRequestDto: CreateTestRequestDto): Promise { const baselineData = convertBaselineDataToQuery(createTestRequestDto); diff --git a/src/test-variations/test-variations.controller.ts b/src/test-variations/test-variations.controller.ts index 450b6e85..b3faa0c5 100644 --- a/src/test-variations/test-variations.controller.ts +++ b/src/test-variations/test-variations.controller.ts @@ -48,4 +48,16 @@ export class TestVariationsController { updateComment(@Param('id', new ParseUUIDPipe()) id: string, @Body() body: CommentDto): Promise { return this.testVariations.updateComment(id, body); } + + @Get('merge') + @ApiQuery({ name: 'projectId', required: true }) + @ApiQuery({ name: 'branchName', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + merge( + @Query('projectId', new ParseUUIDPipe()) projectId: string, + @Query('branchName') branchName: string + ): Promise { + return this.testVariations.merge(projectId, branchName); + } } diff --git a/src/test-variations/test-variations.module.ts b/src/test-variations/test-variations.module.ts index 337832e8..6016c786 100644 --- a/src/test-variations/test-variations.module.ts +++ b/src/test-variations/test-variations.module.ts @@ -2,10 +2,13 @@ import { Module } from '@nestjs/common'; import { TestVariationsService } from './test-variations.service'; import { TestVariationsController } from './test-variations.controller'; import { PrismaService } from '../prisma/prisma.service'; +import { TestRunsService } from '../test-runs/test-runs.service'; +import { EventsGateway } from '../events/events.gateway'; +import { BuildsService } from '../builds/builds.service'; @Module({ - providers: [TestVariationsService, PrismaService], + providers: [TestVariationsService, PrismaService, TestRunsService, EventsGateway, BuildsService], controllers: [TestVariationsController], - exports: [TestVariationsService] + exports: [TestVariationsService], }) export class TestVariationsModule {} diff --git a/src/test-variations/test-variations.service.spec.ts b/src/test-variations/test-variations.service.spec.ts index c2b023c9..44151e47 100644 --- a/src/test-variations/test-variations.service.spec.ts +++ b/src/test-variations/test-variations.service.spec.ts @@ -4,12 +4,16 @@ import { PrismaService } from '../prisma/prisma.service'; import { CreateTestRequestDto } from '../test-runs/dto/create-test-request.dto'; import { StaticService } from '../shared/static/static.service'; import { IgnoreAreaDto } from '../test-runs/dto/ignore-area.dto'; -import { TestVariation, Baseline, Project } from '@prisma/client'; +import { TestVariation, Baseline, Project, Build } from '@prisma/client'; import { CommentDto } from '../shared/dto/comment.dto'; import { convertBaselineDataToQuery } from '../shared/dto/baseline-data.dto'; +import { PNG } from 'pngjs'; +import { BuildsService } from '../builds/builds.service'; +import { TestRunsService } from '../test-runs/test-runs.service'; const initModule = async ({ imageDeleteMock = jest.fn(), + getImageMock = jest.fn(), variationFindOneMock = jest.fn, variationFindManyMock = jest.fn().mockReturnValue([]), variationCreateMock = jest.fn(), @@ -17,6 +21,8 @@ const initModule = async ({ variationDeleteMock = jest.fn(), baselineDeleteMock = jest.fn(), projectFindOneMock = jest.fn(), + buildCreateMock = jest.fn(), + testRunCreateMock = jest.fn(), }) => { const module: TestingModule = await Test.createTestingModule({ providers: [ @@ -24,9 +30,22 @@ const initModule = async ({ { provide: StaticService, useValue: { + getImage: getImageMock, deleteImage: imageDeleteMock, }, }, + { + provide: BuildsService, + useValue: { + create: buildCreateMock, + }, + }, + { + provide: TestRunsService, + useValue: { + create: testRunCreateMock, + }, + }, { provide: PrismaService, useValue: { @@ -353,4 +372,118 @@ describe('TestVariationsService', () => { }, }); }); + + it('merge', async () => { + const mergedBranch = 'develop'; + const project: Project = { + id: 'some id', + name: 'some name', + mainBranchName: 'master', + updatedAt: new Date(), + createdAt: new Date(), + }; + const build: Build = { + id: 'a9385fc1-884d-4f9f-915e-40da0e7773d5', + number: null, + branchName: project.mainBranchName, + status: null, + projectId: project.id, + updatedAt: new Date(), + createdAt: new Date(), + userId: null, + }; + const testVariation: TestVariation = { + id: '123', + projectId: project.id, + name: 'Test name', + baselineName: 'baselineName', + os: 'OS', + browser: 'browser', + viewport: 'viewport', + device: 'device', + ignoreAreas: '[]', + comment: 'some comment', + createdAt: new Date(), + updatedAt: new Date(), + branchName: mergedBranch, + }; + const testVariationNoBaseline: TestVariation = { + id: '123', + projectId: project.id, + name: 'Test name', + baselineName: null, + os: 'OS', + browser: 'browser', + viewport: 'viewport', + device: 'device', + ignoreAreas: '[]', + comment: 'some comment', + createdAt: new Date(), + updatedAt: new Date(), + branchName: mergedBranch, + }; + const testVariationMainBranch: TestVariation = { + id: '123', + projectId: project.id, + name: 'Test name', + baselineName: 'baselineName', + os: 'OS', + browser: 'browser', + viewport: 'viewport', + device: 'device', + ignoreAreas: '[]', + comment: 'some comment', + createdAt: new Date(), + updatedAt: new Date(), + branchName: project.mainBranchName, + }; + const projectFindOneMock = jest.fn().mockResolvedValueOnce(project); + const buildCreateMock = jest.fn().mockResolvedValueOnce(build); + const variationFindManyMock = jest.fn().mockResolvedValueOnce([testVariation, testVariationNoBaseline]); + const image = new PNG({ + width: 10, + height: 10, + }); + const getImageMock = jest + .fn() + .mockReturnValueOnce(image) + .mockReturnValueOnce(null); + const findOrCreateMock = jest.fn().mockResolvedValueOnce(testVariationMainBranch); + const testRunCreateMock = jest.fn(); + const service = await initModule({ + projectFindOneMock, + buildCreateMock, + testRunCreateMock, + variationFindManyMock, + getImageMock, + }); + service.findOrCreate = findOrCreateMock; + + await service.merge(project.id, mergedBranch); + + expect(projectFindOneMock).toHaveBeenCalledWith({ where: { id: project.id } }); + expect(buildCreateMock).toHaveBeenCalledWith({ + branchName: project.mainBranchName, + project: project.id, + }); + expect(variationFindManyMock).toHaveBeenCalledWith({ + where: { projectId: project.id, branchName: mergedBranch }, + }); + expect(getImageMock).toHaveBeenCalledWith(testVariation.baselineName); + expect(service.findOrCreate).toHaveBeenCalledWith(project.id, { + name: testVariation.name, + os: testVariation.os, + device: testVariation.device, + browser: testVariation.browser, + viewport: testVariation.viewport, + branchName: project.mainBranchName, + }); + expect(testRunCreateMock).toHaveBeenCalledWith(testVariationMainBranch, { + ...testVariation, + buildId: build.id, + imageBase64: PNG.sync.write(image).toString('base64'), + diffTollerancePercent: 0, + merge: true, + }); + }); }); diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index 8e4b9cc4..63e64169 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -1,14 +1,25 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Inject, forwardRef } from '@nestjs/common'; import { IgnoreAreaDto } from '../test-runs/dto/ignore-area.dto'; import { PrismaService } from '../prisma/prisma.service'; -import { TestVariation, Baseline } from '@prisma/client'; +import { TestVariation, Baseline, Project, Build } from '@prisma/client'; import { StaticService } from '../shared/static/static.service'; import { CommentDto } from '../shared/dto/comment.dto'; -import { BaselineDataDto } from '../shared/dto/baseline-data.dto'; +import { BaselineDataDto, convertBaselineDataToQuery } from '../shared/dto/baseline-data.dto'; +import { BuildsService } from '../builds/builds.service'; +import { TestRunsService } from '../test-runs/test-runs.service'; +import { PNG } from 'pngjs'; +import { CreateTestRequestDto } from 'src/test-runs/dto/create-test-request.dto'; @Injectable() export class TestVariationsService { - constructor(private prismaService: PrismaService, private staticService: StaticService) {} + constructor( + private prismaService: PrismaService, + private staticService: StaticService, + @Inject(forwardRef(() => BuildsService)) + private buildsService: BuildsService, + @Inject(forwardRef(() => TestRunsService)) + private testRunsService: TestRunsService + ) {} async getDetails(id: string): Promise { return this.prismaService.testVariation.findOne({ @@ -106,4 +117,51 @@ export class TestVariationsService { where: { id }, }); } + + async merge(projectId: string, branchName: string): Promise { + const project: Project = await this.prismaService.project.findOne({ where: { id: projectId } }); + + // create build + const build: Build = await this.buildsService.create({ + branchName: project.mainBranchName, + project: projectId, + }); + + // find side branch variations + const testVariations: TestVariation[] = await this.prismaService.testVariation.findMany({ + where: { projectId, branchName }, + }); + + // compare to main branch variations + await Promise.all( + testVariations.map(async sideBranchTestVariation => { + const baseline = this.staticService.getImage(sideBranchTestVariation.baselineName); + if (baseline) { + try { + let imageBase64 = PNG.sync.write(baseline).toString('base64'); + + // get main branch variation + const baselineData = convertBaselineDataToQuery({ + ...sideBranchTestVariation, + branchName: project.mainBranchName, + }); + const mainBranchTestVariation = await this.findOrCreate(projectId, baselineData); + + // get side branch request + const createTestRequestDto: CreateTestRequestDto = { + ...sideBranchTestVariation, + buildId: build.id, + imageBase64, + diffTollerancePercent: 0, + merge: true, + }; + + return this.testRunsService.create(mainBranchTestVariation, createTestRequestDto); + } catch (err) { + console.log(err); + } + } + }) + ); + } } From e210f5333bfc1c24c724292e234aaf39fb7759f0 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Thu, 16 Jul 2020 23:01:34 +0200 Subject: [PATCH 12/16] EventsGateway moved to shared --- src/builds/builds.module.ts | 6 +++--- src/builds/builds.service.spec.ts | 2 +- src/builds/builds.service.ts | 2 +- src/{ => shared}/events/events.gateway.ts | 2 +- src/shared/shared.module.ts | 5 +++-- src/test-runs/test-runs.module.ts | 3 +-- src/test-runs/test-runs.service.spec.ts | 2 +- src/test-runs/test-runs.service.ts | 2 +- src/test-variations/test-variations.module.ts | 3 +-- 9 files changed, 13 insertions(+), 14 deletions(-) rename src/{ => shared}/events/events.gateway.ts (90%) diff --git a/src/builds/builds.module.ts b/src/builds/builds.module.ts index a45fc86a..6187a0b1 100644 --- a/src/builds/builds.module.ts +++ b/src/builds/builds.module.ts @@ -4,11 +4,11 @@ import { BuildsController } from './builds.controller'; import { UsersModule } from '../users/users.module'; import { PrismaService } from '../prisma/prisma.service'; import { TestRunsModule } from '../test-runs/test-runs.module'; -import { EventsGateway } from '../events/events.gateway'; +import { SharedModule } from '../shared/shared.module'; @Module({ - imports: [UsersModule, TestRunsModule], - providers: [BuildsService, PrismaService, EventsGateway], + imports: [SharedModule, UsersModule, TestRunsModule], + providers: [BuildsService, PrismaService], controllers: [BuildsController], exports: [BuildsService], }) diff --git a/src/builds/builds.service.spec.ts b/src/builds/builds.service.spec.ts index 56ea653f..6160e21d 100644 --- a/src/builds/builds.service.spec.ts +++ b/src/builds/builds.service.spec.ts @@ -2,7 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { BuildsService } from './builds.service'; import { PrismaService } from '../prisma/prisma.service'; import { TestRunsService } from '../test-runs/test-runs.service'; -import { EventsGateway } from '../events/events.gateway'; +import { EventsGateway } from '../shared/events/events.gateway'; import { CreateBuildDto } from './dto/build-create.dto'; import { Build, TestRun, Project } from '@prisma/client'; import { mocked } from 'ts-jest/utils'; diff --git a/src/builds/builds.service.ts b/src/builds/builds.service.ts index 2cc78060..a9a50038 100644 --- a/src/builds/builds.service.ts +++ b/src/builds/builds.service.ts @@ -3,7 +3,7 @@ import { CreateBuildDto } from './dto/build-create.dto'; import { PrismaService } from '../prisma/prisma.service'; import { Build, Project } from '@prisma/client'; import { TestRunsService } from '../test-runs/test-runs.service'; -import { EventsGateway } from '../events/events.gateway'; +import { EventsGateway } from '../shared/events/events.gateway'; import { BuildDto } from './dto/build.dto'; import uuidAPIKey from 'uuid-apikey'; diff --git a/src/events/events.gateway.ts b/src/shared/events/events.gateway.ts similarity index 90% rename from src/events/events.gateway.ts rename to src/shared/events/events.gateway.ts index 6069f3e8..16775414 100644 --- a/src/events/events.gateway.ts +++ b/src/shared/events/events.gateway.ts @@ -1,7 +1,7 @@ import { WebSocketGateway, WebSocketServer } from '@nestjs/websockets'; import { Server } from 'socket.io'; import { TestRun } from '@prisma/client'; -import { BuildDto } from '../builds/dto/build.dto'; +import { BuildDto } from '../../builds/dto/build.dto'; @WebSocketGateway() export class EventsGateway { diff --git a/src/shared/shared.module.ts b/src/shared/shared.module.ts index a1adbe0f..bc2de924 100644 --- a/src/shared/shared.module.ts +++ b/src/shared/shared.module.ts @@ -1,10 +1,11 @@ import { Global, Module } from '@nestjs/common'; import { StaticService } from './static/static.service'; +import { EventsGateway } from 'src/shared/events/events.gateway'; @Global() @Module({ - providers: [StaticService], - exports: [StaticService], + providers: [StaticService, EventsGateway], + exports: [StaticService, EventsGateway], imports: [], controllers: [], }) diff --git a/src/test-runs/test-runs.module.ts b/src/test-runs/test-runs.module.ts index 87349b1b..579466d0 100644 --- a/src/test-runs/test-runs.module.ts +++ b/src/test-runs/test-runs.module.ts @@ -3,12 +3,11 @@ import { TestRunsService } from './test-runs.service'; import { SharedModule } from '../shared/shared.module'; import { PrismaService } from '../prisma/prisma.service'; import { TestRunsController } from './test-runs.controller'; -import { EventsGateway } from '../events/events.gateway'; import { TestVariationsModule } from '../test-variations/test-variations.module'; @Module({ imports: [SharedModule, TestVariationsModule], - providers: [TestRunsService, PrismaService, EventsGateway], + providers: [TestRunsService, PrismaService], exports: [TestRunsService], controllers: [TestRunsController] }) diff --git a/src/test-runs/test-runs.service.spec.ts b/src/test-runs/test-runs.service.spec.ts index bcead47c..50df32a4 100644 --- a/src/test-runs/test-runs.service.spec.ts +++ b/src/test-runs/test-runs.service.spec.ts @@ -10,7 +10,7 @@ import { CreateTestRequestDto } from './dto/create-test-request.dto'; import { TestRunResultDto } from './dto/testRunResult.dto'; import { DiffResult } from './diffResult'; import { IgnoreAreaDto } from './dto/ignore-area.dto'; -import { EventsGateway } from '../events/events.gateway'; +import { EventsGateway } from '../shared/events/events.gateway'; import { CommentDto } from '../shared/dto/comment.dto'; import { BuildDto } from '../builds/dto/build.dto'; import { TestVariationsService } from '../test-variations/test-variations.service'; diff --git a/src/test-runs/test-runs.service.ts b/src/test-runs/test-runs.service.ts index 67ce648b..c21107db 100644 --- a/src/test-runs/test-runs.service.ts +++ b/src/test-runs/test-runs.service.ts @@ -7,7 +7,7 @@ import { StaticService } from '../shared/static/static.service'; import { PrismaService } from '../prisma/prisma.service'; import { TestRun, TestStatus, TestVariation } from '@prisma/client'; import { DiffResult } from './diffResult'; -import { EventsGateway } from '../events/events.gateway'; +import { EventsGateway } from '../shared/events/events.gateway'; import { CommentDto } from '../shared/dto/comment.dto'; import { BuildDto } from '../builds/dto/build.dto'; import { TestRunResultDto } from '../test-runs/dto/testRunResult.dto'; diff --git a/src/test-variations/test-variations.module.ts b/src/test-variations/test-variations.module.ts index 6016c786..0e8c23f3 100644 --- a/src/test-variations/test-variations.module.ts +++ b/src/test-variations/test-variations.module.ts @@ -3,11 +3,10 @@ import { TestVariationsService } from './test-variations.service'; import { TestVariationsController } from './test-variations.controller'; import { PrismaService } from '../prisma/prisma.service'; import { TestRunsService } from '../test-runs/test-runs.service'; -import { EventsGateway } from '../events/events.gateway'; import { BuildsService } from '../builds/builds.service'; @Module({ - providers: [TestVariationsService, PrismaService, TestRunsService, EventsGateway, BuildsService], + providers: [TestVariationsService, PrismaService, TestRunsService, BuildsService], controllers: [TestVariationsController], exports: [TestVariationsService], }) From 4e4c9ad5c0bdd573fcd632a672b34ad116daa451 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Thu, 16 Jul 2020 23:55:55 +0200 Subject: [PATCH 13/16] merge now returns buildDto --- .../test-variations.controller.ts | 17 ++++++++--------- src/test-variations/test-variations.service.ts | 7 +++++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/test-variations/test-variations.controller.ts b/src/test-variations/test-variations.controller.ts index b3faa0c5..0c872504 100644 --- a/src/test-variations/test-variations.controller.ts +++ b/src/test-variations/test-variations.controller.ts @@ -1,11 +1,12 @@ import { Controller, ParseUUIDPipe, Get, UseGuards, Param, Query, Put, Body } from '@nestjs/common'; -import { ApiTags, ApiParam, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; +import { ApiTags, ApiParam, ApiBearerAuth, ApiQuery, ApiOkResponse } from '@nestjs/swagger'; import { TestVariationsService } from './test-variations.service'; import { TestVariation, Baseline } from '@prisma/client'; import { JwtAuthGuard } from '../auth/guards/auth.guard'; import { PrismaService } from '../prisma/prisma.service'; import { IgnoreAreaDto } from '../test-runs/dto/ignore-area.dto'; import { CommentDto } from '../shared/dto/comment.dto'; +import { BuildDto } from 'src/builds/dto/build.dto'; @ApiTags('test-variations') @Controller('test-variations') @@ -16,17 +17,17 @@ export class TestVariationsController { @ApiQuery({ name: 'projectId', required: true }) @ApiBearerAuth() @UseGuards(JwtAuthGuard) - getList(@Query('projectId', new ParseUUIDPipe()) projectId): Promise { + getList(@Query('projectId', new ParseUUIDPipe()) projectId: string): Promise { return this.prismaService.testVariation.findMany({ where: { projectId }, }); } - @Get(':id') + @Get('details/:id') @ApiQuery({ name: 'id', required: true }) @ApiBearerAuth() @UseGuards(JwtAuthGuard) - getDetails(@Param('id', new ParseUUIDPipe()) id): Promise { + getDetails(@Param('id', new ParseUUIDPipe()) id: string): Promise { return this.testVariations.getDetails(id); } @@ -49,15 +50,13 @@ export class TestVariationsController { return this.testVariations.updateComment(id, body); } - @Get('merge') + @Get('merge/') @ApiQuery({ name: 'projectId', required: true }) @ApiQuery({ name: 'branchName', required: true }) + @ApiOkResponse({ type: BuildDto }) @ApiBearerAuth() @UseGuards(JwtAuthGuard) - merge( - @Query('projectId', new ParseUUIDPipe()) projectId: string, - @Query('branchName') branchName: string - ): Promise { + merge(@Query('projectId') projectId: string, @Query('branchName') branchName: string): Promise { return this.testVariations.merge(projectId, branchName); } } diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index 63e64169..327f426a 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -9,6 +9,7 @@ import { BuildsService } from '../builds/builds.service'; import { TestRunsService } from '../test-runs/test-runs.service'; import { PNG } from 'pngjs'; import { CreateTestRequestDto } from 'src/test-runs/dto/create-test-request.dto'; +import { BuildDto } from 'src/builds/dto/build.dto'; @Injectable() export class TestVariationsService { @@ -118,11 +119,11 @@ export class TestVariationsService { }); } - async merge(projectId: string, branchName: string): Promise { + async merge(projectId: string, branchName: string): Promise { const project: Project = await this.prismaService.project.findOne({ where: { id: projectId } }); // create build - const build: Build = await this.buildsService.create({ + const build: BuildDto = await this.buildsService.create({ branchName: project.mainBranchName, project: projectId, }); @@ -163,5 +164,7 @@ export class TestVariationsService { } }) ); + + return build; } } From c1d3e244c0af0db6879e36073be0aec90affb3e6 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 18 Jul 2020 13:12:11 +0200 Subject: [PATCH 14/16] Update test-variations.controller.ts --- src/test-variations/test-variations.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test-variations/test-variations.controller.ts b/src/test-variations/test-variations.controller.ts index 0c872504..5f2f265c 100644 --- a/src/test-variations/test-variations.controller.ts +++ b/src/test-variations/test-variations.controller.ts @@ -6,7 +6,7 @@ import { JwtAuthGuard } from '../auth/guards/auth.guard'; import { PrismaService } from '../prisma/prisma.service'; import { IgnoreAreaDto } from '../test-runs/dto/ignore-area.dto'; import { CommentDto } from '../shared/dto/comment.dto'; -import { BuildDto } from 'src/builds/dto/build.dto'; +import { BuildDto } from '../builds/dto/build.dto'; @ApiTags('test-variations') @Controller('test-variations') From b7f2087a8f5f09d4d0f64c0f53dfccc87c186eee Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 18 Jul 2020 14:42:24 +0200 Subject: [PATCH 15/16] test run create now async --- .../test-variations.service.spec.ts | 37 +++++++++++- .../test-variations.service.ts | 56 +++++++++---------- 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/src/test-variations/test-variations.service.spec.ts b/src/test-variations/test-variations.service.spec.ts index 44151e47..8c612f4d 100644 --- a/src/test-variations/test-variations.service.spec.ts +++ b/src/test-variations/test-variations.service.spec.ts @@ -407,6 +407,21 @@ describe('TestVariationsService', () => { updatedAt: new Date(), branchName: mergedBranch, }; + const testVariationSecond: TestVariation = { + id: '123', + projectId: project.id, + name: 'Test name second', + baselineName: 'baselineName', + os: 'OS', + browser: 'browser', + viewport: 'viewport', + device: 'device', + ignoreAreas: '[]', + comment: 'some comment', + createdAt: new Date(), + updatedAt: new Date(), + branchName: mergedBranch, + }; const testVariationNoBaseline: TestVariation = { id: '123', projectId: project.id, @@ -439,7 +454,9 @@ describe('TestVariationsService', () => { }; const projectFindOneMock = jest.fn().mockResolvedValueOnce(project); const buildCreateMock = jest.fn().mockResolvedValueOnce(build); - const variationFindManyMock = jest.fn().mockResolvedValueOnce([testVariation, testVariationNoBaseline]); + const variationFindManyMock = jest + .fn() + .mockResolvedValueOnce([testVariation, testVariationSecond, testVariationNoBaseline]); const image = new PNG({ width: 10, height: 10, @@ -447,8 +464,12 @@ describe('TestVariationsService', () => { const getImageMock = jest .fn() .mockReturnValueOnce(image) + .mockReturnValueOnce(image) .mockReturnValueOnce(null); - const findOrCreateMock = jest.fn().mockResolvedValueOnce(testVariationMainBranch); + const findOrCreateMock = jest + .fn() + .mockResolvedValueOnce(testVariationMainBranch) + .mockResolvedValueOnce(testVariationMainBranch); const testRunCreateMock = jest.fn(); const service = await initModule({ projectFindOneMock, @@ -478,12 +499,22 @@ describe('TestVariationsService', () => { viewport: testVariation.viewport, branchName: project.mainBranchName, }); - expect(testRunCreateMock).toHaveBeenCalledWith(testVariationMainBranch, { + + await new Promise(r => setTimeout(r, 1)); + expect(testRunCreateMock).toHaveBeenNthCalledWith(1, testVariationMainBranch, { ...testVariation, buildId: build.id, imageBase64: PNG.sync.write(image).toString('base64'), diffTollerancePercent: 0, merge: true, }); + expect(testRunCreateMock).toHaveBeenNthCalledWith(2, testVariationMainBranch, { + ...testVariationSecond, + buildId: build.id, + imageBase64: PNG.sync.write(image).toString('base64'), + diffTollerancePercent: 0, + merge: true, + }); + expect(testRunCreateMock).toHaveBeenCalledTimes(2); }); }); diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index 327f426a..cdcb543e 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -134,36 +134,34 @@ export class TestVariationsService { }); // compare to main branch variations - await Promise.all( - testVariations.map(async sideBranchTestVariation => { - const baseline = this.staticService.getImage(sideBranchTestVariation.baselineName); - if (baseline) { - try { - let imageBase64 = PNG.sync.write(baseline).toString('base64'); - - // get main branch variation - const baselineData = convertBaselineDataToQuery({ - ...sideBranchTestVariation, - branchName: project.mainBranchName, - }); - const mainBranchTestVariation = await this.findOrCreate(projectId, baselineData); - - // get side branch request - const createTestRequestDto: CreateTestRequestDto = { - ...sideBranchTestVariation, - buildId: build.id, - imageBase64, - diffTollerancePercent: 0, - merge: true, - }; - - return this.testRunsService.create(mainBranchTestVariation, createTestRequestDto); - } catch (err) { - console.log(err); - } + testVariations.map(async sideBranchTestVariation => { + const baseline = this.staticService.getImage(sideBranchTestVariation.baselineName); + if (baseline) { + try { + let imageBase64 = PNG.sync.write(baseline).toString('base64'); + + // get main branch variation + const baselineData = convertBaselineDataToQuery({ + ...sideBranchTestVariation, + branchName: project.mainBranchName, + }); + const mainBranchTestVariation = await this.findOrCreate(projectId, baselineData); + + // get side branch request + const createTestRequestDto: CreateTestRequestDto = { + ...sideBranchTestVariation, + buildId: build.id, + imageBase64, + diffTollerancePercent: 0, + merge: true, + }; + + return this.testRunsService.create(mainBranchTestVariation, createTestRequestDto); + } catch (err) { + console.log(err); } - }) - ); + } + }); return build; } From f78c7ac3e982b5c28fd5444c3d84bd4c2e2a8b29 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 18 Jul 2020 14:54:42 +0200 Subject: [PATCH 16/16] Update shared.module.ts --- src/shared/shared.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/shared.module.ts b/src/shared/shared.module.ts index bc2de924..1838a6f0 100644 --- a/src/shared/shared.module.ts +++ b/src/shared/shared.module.ts @@ -1,6 +1,6 @@ import { Global, Module } from '@nestjs/common'; import { StaticService } from './static/static.service'; -import { EventsGateway } from 'src/shared/events/events.gateway'; +import { EventsGateway } from '../shared/events/events.gateway'; @Global() @Module({