From 4193f0f13bbd37968078a4c3a4cb52db0acd85fa Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Fri, 22 May 2020 22:44:19 +0200 Subject: [PATCH 01/16] variation controller. Get method added --- .../test-variations.controller.ts | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/test-variations/test-variations.controller.ts b/src/test-variations/test-variations.controller.ts index 3e68f69a..7c15a58b 100644 --- a/src/test-variations/test-variations.controller.ts +++ b/src/test-variations/test-variations.controller.ts @@ -1,9 +1,24 @@ -import { Controller } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { Controller, ParseUUIDPipe, Get, UseGuards, Param, Query } from '@nestjs/common'; +import { ApiTags, ApiParam, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; import { TestVariationsService } from './test-variations.service'; +import { TestVariation } from '@prisma/client'; +import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; +import { PrismaService } from 'src/prisma/prisma.service'; @ApiTags('test-variations') @Controller('test-variations') export class TestVariationsController { - constructor(private testVariations: TestVariationsService) {} + constructor(private testVariations: TestVariationsService, private prismaService: PrismaService) { } + + @Get() + @ApiQuery({ name: 'projectId', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + getList( + @Query('projectId', new ParseUUIDPipe()) projectId, + ): Promise { + return this.prismaService.testVariation.findMany({ + where: { projectId } + }); + } } From 283b3e28e8dd470b5630274d2fcf1799e2af8dab Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 23 May 2020 13:07:46 +0200 Subject: [PATCH 02/16] TestRun added with variation data --- .../README.md | 66 ++++++++ .../schema.prisma | 91 ++++++++++ .../steps.json | 157 ++++++++++++++++++ prisma/migrations/migrate.lock | 3 +- prisma/schema.prisma | 12 +- 5 files changed, 326 insertions(+), 3 deletions(-) create mode 100644 prisma/migrations/20200523130656-test-run-owns-variation-data/README.md create mode 100644 prisma/migrations/20200523130656-test-run-owns-variation-data/schema.prisma create mode 100644 prisma/migrations/20200523130656-test-run-owns-variation-data/steps.json diff --git a/prisma/migrations/20200523130656-test-run-owns-variation-data/README.md b/prisma/migrations/20200523130656-test-run-owns-variation-data/README.md new file mode 100644 index 00000000..1dfc1d9c --- /dev/null +++ b/prisma/migrations/20200523130656-test-run-owns-variation-data/README.md @@ -0,0 +1,66 @@ +# Migration `20200523130656-test-run-owns-variation-data` + +This migration has been generated by Pavel Strunkin at 5/23/2020, 1:06:56 PM. +You can check out the [state of the schema](./schema.prisma) after the migration. + +## Database Steps + +```sql +ALTER TABLE "public"."TestRun" ADD COLUMN "baselineName" text , +ADD COLUMN "browser" text , +ADD COLUMN "device" text , +ADD COLUMN "ignoreAreas" text NOT NULL DEFAULT '[]', +ADD COLUMN "name" text NOT NULL DEFAULT '', +ADD COLUMN "os" text , +ADD COLUMN "viewport" text ; +``` + +## Changes + +```diff +diff --git schema.prisma schema.prisma +migration 20200503001556-init..20200523130656-test-run-owns-variation-data +--- datamodel.dml ++++ datamodel.dml +@@ -3,9 +3,9 @@ + } + datasource db { + provider = "postgresql" +- url = "***" ++ url = env("DATABASE_URL") + } + model Build { + id String @default(uuid()) @id +@@ -14,12 +14,12 @@ + status String? + testRuns TestRun[] + projectId String + project Project @relation(fields: [projectId], references: [id]) +- // userId String +- // user User @relation(fields: [userId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) ++ user User? @relation(fields: [userId], references: [id]) ++ userId String? + } + model Project { + id String @default(uuid()) @id +@@ -43,8 +43,16 @@ + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) ++ // Test variation data ++ name String @default("") ++ browser String? ++ device String? ++ os String? ++ viewport String? ++ baselineName String? ++ ignoreAreas String @default("[]") + } + model TestVariation { + id String @default(uuid()) @id +``` + + diff --git a/prisma/migrations/20200523130656-test-run-owns-variation-data/schema.prisma b/prisma/migrations/20200523130656-test-run-owns-variation-data/schema.prisma new file mode 100644 index 00000000..8dd1fa3d --- /dev/null +++ b/prisma/migrations/20200523130656-test-run-owns-variation-data/schema.prisma @@ -0,0 +1,91 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = "***" +} + +model Build { + id String @default(uuid()) @id + number Int? + branchName String? + status String? + testRuns TestRun[] + projectId String + project Project @relation(fields: [projectId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + user User? @relation(fields: [userId], references: [id]) + userId String? +} + +model Project { + id String @default(uuid()) @id + name String + builds Build[] + testVariations TestVariation[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +model TestRun { + id String @default(uuid()) @id + imageName String + diffName String? + diffPercent Float? + diffTollerancePercent Float @default(1.0) + pixelMisMatchCount Int? + status TestStatus + buildId String + build Build @relation(fields: [buildId], references: [id]) + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + // Test variation data + name String @default("") + browser String? + device String? + os String? + viewport String? + baselineName String? + ignoreAreas String @default("[]") +} + +model TestVariation { + id String @default(uuid()) @id + name String + browser String? + device String? + os String? + viewport String? + baselineName String? + ignoreAreas String @default("[]") + projectId String + project Project @relation(fields: [projectId], references: [id]) + testRuns TestRun[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +model User { + id String @default(uuid()) @id + email String @unique + password String + firstName String? + lastName String? + apiKey String @unique + isActive Boolean @default(true) + builds Build[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +enum TestStatus { + failed + new + ok + unresolved +} \ No newline at end of file diff --git a/prisma/migrations/20200523130656-test-run-owns-variation-data/steps.json b/prisma/migrations/20200523130656-test-run-owns-variation-data/steps.json new file mode 100644 index 00000000..1e17e222 --- /dev/null +++ b/prisma/migrations/20200523130656-test-run-owns-variation-data/steps.json @@ -0,0 +1,157 @@ +{ + "version": "0.3.14-fixed", + "steps": [ + { + "tag": "CreateField", + "model": "Build", + "field": "user", + "type": "User", + "arity": "Optional" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "Build", + "field": "user" + }, + "directive": "relation" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Build", + "field": "user" + }, + "directive": "relation" + }, + "argument": "fields", + "value": "[userId]" + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Build", + "field": "user" + }, + "directive": "relation" + }, + "argument": "references", + "value": "[id]" + }, + { + "tag": "CreateField", + "model": "Build", + "field": "userId", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "name", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "TestRun", + "field": "name" + }, + "directive": "default" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "TestRun", + "field": "name" + }, + "directive": "default" + }, + "argument": "", + "value": "\"\"" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "browser", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "device", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "os", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "viewport", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "baselineName", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "ignoreAreas", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "TestRun", + "field": "ignoreAreas" + }, + "directive": "default" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "TestRun", + "field": "ignoreAreas" + }, + "directive": "default" + }, + "argument": "", + "value": "\"[]\"" + } + ] +} \ No newline at end of file diff --git a/prisma/migrations/migrate.lock b/prisma/migrations/migrate.lock index 78f2bfe8..19288137 100644 --- a/prisma/migrations/migrate.lock +++ b/prisma/migrations/migrate.lock @@ -3,4 +3,5 @@ # Prisma Migrate lockfile v1 # Read more about conflict resolution here: TODO -20200503001556-init \ No newline at end of file +20200503001556-init +20200523130656-test-run-owns-variation-data \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a12f5e71..1130026d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -15,10 +15,10 @@ model Build { testRuns TestRun[] projectId String project Project @relation(fields: [projectId], references: [id]) - // userId String - // user User @relation(fields: [userId], references: [id]) updatedAt DateTime @updatedAt createdAt DateTime @default(now()) + user User? @relation(fields: [userId], references: [id]) + userId String? } model Project { @@ -44,6 +44,14 @@ model TestRun { testVariation TestVariation @relation(fields: [testVariationId], references: [id]) updatedAt DateTime @updatedAt createdAt DateTime @default(now()) + // Test variation data + name String @default("") + browser String? + device String? + os String? + viewport String? + baselineName String? + ignoreAreas String @default("[]") } model TestVariation { From ca56ed87d7610be8a5fa8d23f208032ec23e8030 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 23 May 2020 13:15:05 +0200 Subject: [PATCH 03/16] TestRun. copy variation data during creation --- package-lock.json | 2 +- src/test-runs/test-runs.service.ts | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4779af0e..55bdde16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vrt-backend", - "version": "0.0.5", + "version": "0.0.8", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/test-runs/test-runs.service.ts b/src/test-runs/test-runs.service.ts index 934b97d1..81d8c3ab 100644 --- a/src/test-runs/test-runs.service.ts +++ b/src/test-runs/test-runs.service.ts @@ -104,17 +104,24 @@ export class TestRunsService { id: createTestRequestDto.buildId, }, }, + name: testVariation.name, + browser: testVariation.browser, + device: testVariation.device, + os: testVariation.os, + viewport: testVariation.viewport, + baselineName: testVariation.baselineName, + ignoreAreas: testVariation.ignoreAreas, diffTollerancePercent: createTestRequestDto.diffTollerancePercent, status: TestStatus.new, }; // get baseline image let baseline: PNGWithMetadata - if (testVariation.baselineName) { + if (testRun.baselineName) { try { - baseline = this.staticService.getImage(testVariation.baselineName) + baseline = this.staticService.getImage(testRun.baselineName) } catch (ex) { - console.log(`Cannot load baseline image: ${testVariation.baselineName}. ${ex}`) + console.log(`Cannot load baseline image: ${testRun.baselineName}. ${ex}`) } } @@ -128,8 +135,8 @@ export class TestRunsService { // compare const pixelMisMatchCount = Pixelmatch( - this.applyIgnoreAreas(baseline, JSON.parse(testVariation.ignoreAreas)), - this.applyIgnoreAreas(image, JSON.parse(testVariation.ignoreAreas)), + this.applyIgnoreAreas(baseline, JSON.parse(testRun.ignoreAreas)), + this.applyIgnoreAreas(image, JSON.parse(testRun.ignoreAreas)), diff.data, baseline.width, baseline.height, From 2dbd2a082b47ac838cda6c1f656f0d3c4c4fae47 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 23 May 2020 13:33:18 +0200 Subject: [PATCH 04/16] refactoring --- src/builds/builds.controller.ts | 4 ++-- src/builds/builds.module.ts | 4 ++-- src/builds/builds.service.ts | 19 +++++++++++------- src/test-runs/test-runs.controller.spec.ts | 18 +++++++++++++++++ src/test-runs/test-runs.controller.ts | 19 ++++++++++++++++++ src/test-runs/test-runs.module.ts | 4 +++- src/test-variations/test-variations.module.ts | 1 - .../test-variations.service.ts | 20 ------------------- src/test/test.controller.ts | 12 +---------- src/test/test.service.ts | 12 ++--------- 10 files changed, 59 insertions(+), 54 deletions(-) create mode 100644 src/test-runs/test-runs.controller.spec.ts create mode 100644 src/test-runs/test-runs.controller.ts diff --git a/src/builds/builds.controller.ts b/src/builds/builds.controller.ts index 19d8297f..adfce9d1 100644 --- a/src/builds/builds.controller.ts +++ b/src/builds/builds.controller.ts @@ -24,8 +24,8 @@ export class BuildsController { @ApiParam({ name: 'id', required: true }) @ApiBearerAuth() @UseGuards(JwtAuthGuard) - getDetails(@Param('id', new ParseUUIDPipe()) id: string): Promise { - return this.buildsService.findById(id); + getDetails(@Param('id', new ParseUUIDPipe()) id: string): Promise { + return this.buildsService.findOne(id); } @Delete(':id') diff --git a/src/builds/builds.module.ts b/src/builds/builds.module.ts index a3cb83fb..67ac98d2 100644 --- a/src/builds/builds.module.ts +++ b/src/builds/builds.module.ts @@ -2,11 +2,11 @@ import { Module } from '@nestjs/common'; import { BuildsService } from './builds.service'; import { BuildsController } from './builds.controller'; import { UsersModule } from 'src/users/users.module'; -import { TestModule } from 'src/test/test.module'; import { PrismaService } from 'src/prisma/prisma.service'; +import { TestRunsModule } from 'src/test-runs/test-runs.module'; @Module({ - imports: [UsersModule, TestModule], + imports: [UsersModule, TestRunsModule], providers: [BuildsService, PrismaService], controllers: [BuildsController], exports: [BuildsService], diff --git a/src/builds/builds.service.ts b/src/builds/builds.service.ts index 10f13cba..a281e768 100644 --- a/src/builds/builds.service.ts +++ b/src/builds/builds.service.ts @@ -1,18 +1,23 @@ import { Injectable } from '@nestjs/common'; import { CreateBuildDto } from './dto/build-create.dto'; -import { TestService } from 'src/test/test.service'; import { PrismaService } from 'src/prisma/prisma.service'; -import { Build, TestRun, TestVariation } from '@prisma/client'; +import { Build, TestRun } from '@prisma/client'; +import { TestRunsService } from 'src/test-runs/test-runs.service'; @Injectable() export class BuildsService { constructor( private prismaService: PrismaService, - private testService: TestService + private testRunsService: TestRunsService ) { } - async findById(id: string): Promise<(TestRun & { testVariation: TestVariation; })[]> { - return this.testService.getTestRunsByBuildId(id); + async findOne(id: string): Promise { + return this.prismaService.build.findOne({ + where: { id }, + include: { + testRuns: true + } + }); } async findMany(projectId: string): Promise { @@ -36,11 +41,11 @@ export class BuildsService { } async remove(id: string): Promise { - const testRuns = await this.findById(id); + const build = await this.findOne(id); try { await Promise.all( - testRuns.map((testRun) => this.testService.deleteTestRun(testRun.id)) + build.testRuns.map((testRun) => this.testRunsService.delete(testRun.id)) ); } catch (err) { console.log(err); diff --git a/src/test-runs/test-runs.controller.spec.ts b/src/test-runs/test-runs.controller.spec.ts new file mode 100644 index 00000000..a3d0569b --- /dev/null +++ b/src/test-runs/test-runs.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { TestRunsController } from './test-runs.controller'; + +describe('TestRuns Controller', () => { + let controller: TestRunsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [TestRunsController], + }).compile(); + + controller = module.get(TestRunsController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/test-runs/test-runs.controller.ts b/src/test-runs/test-runs.controller.ts new file mode 100644 index 00000000..32719809 --- /dev/null +++ b/src/test-runs/test-runs.controller.ts @@ -0,0 +1,19 @@ +import { Controller, Delete, UseGuards, Param, ParseUUIDPipe } from '@nestjs/common'; +import { ApiTags, ApiParam, ApiBearerAuth } from '@nestjs/swagger'; +import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; +import { TestRun } from '@prisma/client'; +import { TestRunsService } from './test-runs.service'; + +@ApiTags('test-runs') +@Controller('test-runs') +export class TestRunsController { + constructor(private testRunsService: TestRunsService) { } + + @Delete('/:id') + @ApiParam({ name: 'id', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + deleteTestRun(@Param('id', new ParseUUIDPipe()) id: string): Promise { + return this.testRunsService.delete(id); + } +} diff --git a/src/test-runs/test-runs.module.ts b/src/test-runs/test-runs.module.ts index 021ac289..5d48c188 100644 --- a/src/test-runs/test-runs.module.ts +++ b/src/test-runs/test-runs.module.ts @@ -2,10 +2,12 @@ import { Module } from '@nestjs/common'; import { TestRunsService } from './test-runs.service'; import { SharedModule } from 'src/shared/shared.module'; import { PrismaService } from 'src/prisma/prisma.service'; +import { TestRunsController } from './test-runs.controller'; @Module({ imports: [SharedModule], providers: [TestRunsService, PrismaService], - exports: [TestRunsService] + exports: [TestRunsService], + controllers: [TestRunsController] }) export class TestRunsModule {} diff --git a/src/test-variations/test-variations.module.ts b/src/test-variations/test-variations.module.ts index 047c7600..7c680a67 100644 --- a/src/test-variations/test-variations.module.ts +++ b/src/test-variations/test-variations.module.ts @@ -5,7 +5,6 @@ import { TestRunsModule } from 'src/test-runs/test-runs.module'; import { PrismaService } from 'src/prisma/prisma.service'; @Module({ - imports: [TestRunsModule], providers: [TestVariationsService, PrismaService], controllers: [TestVariationsController], exports: [TestVariationsService] diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index fc6e4c95..499d77d7 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -1,16 +1,12 @@ import { Injectable } from '@nestjs/common'; import { CreateTestRequestDto } from 'src/test/dto/create-test-request.dto'; import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; -import { TestRunsService } from 'src/test-runs/test-runs.service'; -import { StaticService } from 'src/shared/static/static.service'; import { PrismaService } from 'src/prisma/prisma.service'; import { TestVariation } from '@prisma/client'; @Injectable() export class TestVariationsService { constructor( - private testRunsService: TestRunsService, - private staticService: StaticService, private prismaService: PrismaService, ) {} @@ -52,22 +48,6 @@ export class TestVariationsService { } async remove(id: string): Promise { - const testVariation = await this.prismaService.testVariation.findOne({ - where: { id }, - include: { - testRuns: true, - }, - }); - - try { - await Promise.all( - testVariation.testRuns.map(testRun => this.testRunsService.delete(testRun.id)), - ); - if (testVariation.baselineName) this.staticService.deleteImage(testVariation.baselineName); - } catch (err) { - console.log(err); - } - return this.prismaService.testVariation.delete({ where: { id }, }); diff --git a/src/test/test.controller.ts b/src/test/test.controller.ts index bdea243e..199571b4 100644 --- a/src/test/test.controller.ts +++ b/src/test/test.controller.ts @@ -29,9 +29,7 @@ export class TestController { @UseGuards(JwtAuthGuard) getTestRunDetails( @Param('testRunId', new ParseUUIDPipe()) testRunId: string, - ): Promise { + ): Promise { return this.testService.getTestRunById(testRunId); } @@ -69,12 +67,4 @@ export class TestController { postTestRun(@Body() createTestRequestDto: CreateTestRequestDto): Promise { return this.testService.postTestRun(createTestRequestDto); } - - @Delete('/:testRunId') - @ApiParam({ name: 'testRunId', required: true }) - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - deleteTestRun(@Param('testRunId', new ParseUUIDPipe()) testRunId: string): Promise { - return this.testService.deleteTestRun(testRunId); - } } diff --git a/src/test/test.service.ts b/src/test/test.service.ts index 7f0427a7..c58bd718 100644 --- a/src/test/test.service.ts +++ b/src/test/test.service.ts @@ -13,16 +13,12 @@ export class TestService { private testRunsService: TestRunsService, ) { } - async getTestRunsByBuildId(buildId: string): Promise<(TestRun & { - testVariation: TestVariation; - })[]> { + async getTestRunsByBuildId(buildId: string): Promise { return this.testRunsService.getAll(buildId); } - async getTestRunById(testRunId: string): Promise { + async getTestRunById(testRunId: string): Promise { return this.testRunsService.findOne(testRunId); } @@ -34,10 +30,6 @@ export class TestService { return new TestRunResultDto(testRun, testVariation); } - async deleteTestRun(id: string): Promise { - return this.testRunsService.delete(id); - } - async approveTestRun(testRunId: string): Promise { From a847068f0bfce4bfce440e1cf00362ac6e5dfe62 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 23 May 2020 14:55:25 +0200 Subject: [PATCH 05/16] refactoring --- src/projects/projects.service.ts | 2 +- src/test-runs/test-runs.controller.ts | 30 ++++++++++++- src/test-runs/test-runs.service.ts | 44 ++++++------------- .../test-variations.controller.ts | 14 +++++- .../test-variations.service.ts | 2 +- src/test/test.controller.ts | 37 ---------------- src/test/test.service.ts | 28 ------------ 7 files changed, 57 insertions(+), 100 deletions(-) diff --git a/src/projects/projects.service.ts b/src/projects/projects.service.ts index 4fb0985d..2ca4256e 100644 --- a/src/projects/projects.service.ts +++ b/src/projects/projects.service.ts @@ -51,12 +51,12 @@ export class ProjectsService { }); try { + await Promise.all(project.builds.map(build => this.buildsService.remove(build.id))); await Promise.all( project.testVariations.map(testVariation => this.testVariationsService.remove(testVariation.id), ), ); - await Promise.all(project.builds.map(build => this.buildsService.remove(build.id))); } catch (err) { console.log(err); } diff --git a/src/test-runs/test-runs.controller.ts b/src/test-runs/test-runs.controller.ts index 32719809..21aa09c2 100644 --- a/src/test-runs/test-runs.controller.ts +++ b/src/test-runs/test-runs.controller.ts @@ -1,14 +1,31 @@ -import { Controller, Delete, UseGuards, Param, ParseUUIDPipe } from '@nestjs/common'; +import { Controller, Delete, UseGuards, Param, ParseUUIDPipe, Put, Body, Get } from '@nestjs/common'; import { ApiTags, ApiParam, ApiBearerAuth } from '@nestjs/swagger'; import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; import { TestRun } from '@prisma/client'; import { TestRunsService } from './test-runs.service'; +import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; @ApiTags('test-runs') @Controller('test-runs') export class TestRunsController { constructor(private testRunsService: TestRunsService) { } + @Get('approve/:id') + @ApiParam({ name: 'id', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + approveTestRun(@Param('id', new ParseUUIDPipe()) id: string): Promise { + return this.testRunsService.approve(id); + } + + @Get('reject/:id') + @ApiParam({ name: 'id', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + rejectTestRun(@Param('id', new ParseUUIDPipe()) id: string): Promise { + return this.testRunsService.reject(id); + } + @Delete('/:id') @ApiParam({ name: 'id', required: true }) @ApiBearerAuth() @@ -16,4 +33,15 @@ export class TestRunsController { deleteTestRun(@Param('id', new ParseUUIDPipe()) id: string): Promise { return this.testRunsService.delete(id); } + + @Put('ignoreArea/:testRunId') + @ApiParam({ name: 'testRunId', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + updateIgnoreAreas( + @Param('testRunId', new ParseUUIDPipe()) testRunId: string, + @Body() ignoreAreas: IgnoreAreaDto[], + ): Promise { + return this.testRunsService.updateIgnoreAreas(testRunId, ignoreAreas); + } } diff --git a/src/test-runs/test-runs.service.ts b/src/test-runs/test-runs.service.ts index 81d8c3ab..8601c324 100644 --- a/src/test-runs/test-runs.service.ts +++ b/src/test-runs/test-runs.service.ts @@ -11,20 +11,6 @@ import { TestRun, TestStatus, TestVariation, TestRunCreateInput } from '@prisma/ export class TestRunsService { constructor(private prismaService: PrismaService, private staticService: StaticService) { } - async getAll(buildId: string): Promise<(TestRun & { - testVariation: TestVariation; - })[]> { - return this.prismaService.testRun.findMany({ - where: { buildId }, - include: { - testVariation: true, - }, - orderBy: { - createdAt: 'asc', - }, - }); - } - async findOne(id: string): Promise { @@ -36,16 +22,9 @@ export class TestRunsService { }); } - async approve(id: string): Promise { + async approve(id: string): Promise { const testRun = await this.findOne(id); - // remove old baseline - if (testRun.testVariation.baselineName) { - this.staticService.deleteImage(testRun.testVariation.baselineName); - } - // save new baseline const baseline = this.staticService.getImage(testRun.imageName) const imageName = `${Date.now()}.baseline.png`; @@ -53,10 +32,8 @@ export class TestRunsService { return this.prismaService.testRun.update({ where: { id }, - include: { - testVariation: true, - }, data: { + baselineName: imageName, status: TestStatus.ok, testVariation: { update: { @@ -67,14 +44,9 @@ export class TestRunsService { }); } - async reject(id: string): Promise { + async reject(id: string): Promise { return this.prismaService.testRun.update({ where: { id }, - include: { - testVariation: true, - }, data: { status: TestStatus.failed, }, @@ -185,6 +157,16 @@ export class TestRunsService { }); } + async updateIgnoreAreas(id: string, ignoreAreas: IgnoreAreaDto[]): Promise { + return this.prismaService.testRun + .update({ + where: { id }, + data: { + ignoreAreas: JSON.stringify(ignoreAreas), + }, + }); + } + private applyIgnoreAreas(image: PNG, ignoreAreas: IgnoreAreaDto[]): Buffer { ignoreAreas.forEach(area => { for (let y = area.y; y < area.y + area.height; y++) { diff --git a/src/test-variations/test-variations.controller.ts b/src/test-variations/test-variations.controller.ts index 7c15a58b..9b9c29b1 100644 --- a/src/test-variations/test-variations.controller.ts +++ b/src/test-variations/test-variations.controller.ts @@ -1,9 +1,10 @@ -import { Controller, ParseUUIDPipe, Get, UseGuards, Param, Query } from '@nestjs/common'; +import { Controller, ParseUUIDPipe, Get, UseGuards, Param, Query, Put, Body } from '@nestjs/common'; import { ApiTags, ApiParam, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; import { TestVariationsService } from './test-variations.service'; import { TestVariation } from '@prisma/client'; import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; import { PrismaService } from 'src/prisma/prisma.service'; +import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; @ApiTags('test-variations') @Controller('test-variations') @@ -21,4 +22,15 @@ export class TestVariationsController { where: { projectId } }); } + + @Put('ignoreArea/:variationId') + @ApiParam({ name: 'variationId', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + updateIgnoreAreas( + @Param('variationId', new ParseUUIDPipe()) variationId: string, + @Body() ignoreAreas: IgnoreAreaDto[], + ): Promise { + return this.testVariations.updateIgnoreAreas(variationId, ignoreAreas); + } } diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index 499d77d7..b602d796 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -8,7 +8,7 @@ import { TestVariation } from '@prisma/client'; export class TestVariationsService { constructor( private prismaService: PrismaService, - ) {} + ) { } async findOrCreate(createTestDto: CreateTestRequestDto): Promise { const data = { diff --git a/src/test/test.controller.ts b/src/test/test.controller.ts index 199571b4..cce95cbd 100644 --- a/src/test/test.controller.ts +++ b/src/test/test.controller.ts @@ -23,43 +23,6 @@ import { TestRunResultDto } from './dto/testRunResult.dto'; export class TestController { constructor(private testService: TestService) { } - @Get(':testRunId') - @ApiParam({ name: 'testRunId', required: true }) - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - getTestRunDetails( - @Param('testRunId', new ParseUUIDPipe()) testRunId: string, - ): Promise { - return this.testService.getTestRunById(testRunId); - } - - @Get('approve/:testRunId') - @ApiParam({ name: 'testRunId', required: true }) - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - approveTestRun(@Param('testRunId', new ParseUUIDPipe()) testRunId: string): Promise { - return this.testService.approveTestRun(testRunId); - } - - @Get('reject/:testRunId') - @ApiParam({ name: 'testRunId', required: true }) - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - rejectTestRun(@Param('testRunId', new ParseUUIDPipe()) testRunId: string): Promise { - return this.testService.rejectTestRun(testRunId); - } - - @Put('ignoreArea/:variationId') - @ApiParam({ name: 'variationId', required: true }) - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - updateIgnoreAreas( - @Param('variationId', new ParseUUIDPipe()) variationId: string, - @Body() ignoreAreas: IgnoreAreaDto[], - ): Promise { - return this.testService.updateIgnoreAreas(variationId, ignoreAreas); - } - @Post() @ApiSecurity('api_key') @ApiOkResponse({ type: TestRunResultDto }) diff --git a/src/test/test.service.ts b/src/test/test.service.ts index c58bd718..f7e40b6d 100644 --- a/src/test/test.service.ts +++ b/src/test/test.service.ts @@ -13,15 +13,6 @@ export class TestService { private testRunsService: TestRunsService, ) { } - async getTestRunsByBuildId(buildId: string): Promise { - - return this.testRunsService.getAll(buildId); - } - - async getTestRunById(testRunId: string): Promise { - return this.testRunsService.findOne(testRunId); - } - async postTestRun(createTestRequestDto: CreateTestRequestDto): Promise { const testVariation = await this.testVariationService.findOrCreate(createTestRequestDto); @@ -29,23 +20,4 @@ export class TestService { return new TestRunResultDto(testRun, testVariation); } - - async approveTestRun(testRunId: string): Promise { - return this.testRunsService.approve(testRunId); - } - - async rejectTestRun(testRunId: string): Promise { - return this.testRunsService.reject(testRunId); - } - - async updateIgnoreAreas( - testRunId: string, - ignoreAreas: IgnoreAreaDto[], - ): Promise { - return this.testVariationService.updateIgnoreAreas(testRunId, ignoreAreas); - } } From a3fb85b562a572ebbcf17b821ce2ae7dad4eff5e Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 23 May 2020 15:52:27 +0200 Subject: [PATCH 06/16] refactoring --- src/builds/builds.controller.ts | 11 ++++++----- src/projects/projects.controller.ts | 10 +--------- src/test-runs/test-runs.controller.ts | 12 ++++++++++-- src/test-runs/test-runs.service.ts | 6 ++++++ 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/builds/builds.controller.ts b/src/builds/builds.controller.ts index adfce9d1..c4c9e62f 100644 --- a/src/builds/builds.controller.ts +++ b/src/builds/builds.controller.ts @@ -7,10 +7,11 @@ import { Param, ParseUUIDPipe, Delete, + Query, } from '@nestjs/common'; import { BuildsService } from './builds.service'; import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; -import { ApiBearerAuth, ApiTags, ApiParam, ApiSecurity } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiTags, ApiParam, ApiSecurity, ApiQuery } from '@nestjs/swagger'; import { CreateBuildDto } from './dto/build-create.dto'; import { ApiGuard } from 'src/auth/guards/api.guard'; import { Build, TestRun } from '@prisma/client'; @@ -20,12 +21,12 @@ import { Build, TestRun } from '@prisma/client'; export class BuildsController { constructor(private buildsService: BuildsService) { } - @Get(':id') - @ApiParam({ name: 'id', required: true }) + @Get() + @ApiQuery({ name: 'projectId', required: true }) @ApiBearerAuth() @UseGuards(JwtAuthGuard) - getDetails(@Param('id', new ParseUUIDPipe()) id: string): Promise { - return this.buildsService.findOne(id); + get(@Query('projectId', new ParseUUIDPipe()) projectId: string): Promise { + return this.buildsService.findMany(projectId); } @Delete(':id') diff --git a/src/projects/projects.controller.ts b/src/projects/projects.controller.ts index c5d53935..f9748b98 100644 --- a/src/projects/projects.controller.ts +++ b/src/projects/projects.controller.ts @@ -18,7 +18,7 @@ import { BuildsService } from 'src/builds/builds.service'; @Controller('projects') @ApiTags('projects') export class ProjectsController { - constructor(private projectsService: ProjectsService, private buildsService: BuildsService) {} + constructor(private projectsService: ProjectsService) {} @Get() @ApiBearerAuth() @@ -27,14 +27,6 @@ export class ProjectsController { return this.projectsService.findAll(); } - @Get(':id') - @ApiParam({ name: 'id', required: true }) - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - getBuilds(@Param('id', new ParseUUIDPipe()) id: string): Promise { - return this.buildsService.findMany(id); - } - @Post() @ApiBearerAuth() @UseGuards(JwtAuthGuard) diff --git a/src/test-runs/test-runs.controller.ts b/src/test-runs/test-runs.controller.ts index 21aa09c2..e6a27bc8 100644 --- a/src/test-runs/test-runs.controller.ts +++ b/src/test-runs/test-runs.controller.ts @@ -1,5 +1,5 @@ -import { Controller, Delete, UseGuards, Param, ParseUUIDPipe, Put, Body, Get } from '@nestjs/common'; -import { ApiTags, ApiParam, ApiBearerAuth } from '@nestjs/swagger'; +import { Controller, Delete, UseGuards, Param, ParseUUIDPipe, Put, Body, Get, Query } from '@nestjs/common'; +import { ApiTags, ApiParam, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; import { TestRun } from '@prisma/client'; import { TestRunsService } from './test-runs.service'; @@ -10,6 +10,14 @@ import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; export class TestRunsController { constructor(private testRunsService: TestRunsService) { } + @Get() + @ApiQuery({ name: 'buildId', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + get(@Query('buildId', new ParseUUIDPipe()) buildId: string): Promise { + return this.testRunsService.findMany(buildId); + } + @Get('approve/:id') @ApiParam({ name: 'id', required: true }) @ApiBearerAuth() diff --git a/src/test-runs/test-runs.service.ts b/src/test-runs/test-runs.service.ts index 8601c324..e0439e9d 100644 --- a/src/test-runs/test-runs.service.ts +++ b/src/test-runs/test-runs.service.ts @@ -11,6 +11,12 @@ import { TestRun, TestStatus, TestVariation, TestRunCreateInput } from '@prisma/ export class TestRunsService { constructor(private prismaService: PrismaService, private staticService: StaticService) { } + async findMany(buildId: string): Promise { + return this.prismaService.testRun.findMany({ + where: { buildId }, + }); + } + async findOne(id: string): Promise { From 402b5d9621e656eef045891b6a26792007552704 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 23 May 2020 18:29:17 +0200 Subject: [PATCH 07/16] tests are fixed --- package.json | 2 +- src/auth/auth.module.ts | 3 +-- src/auth/guards/api.guard.ts | 2 +- src/builds/builds.controller.spec.ts | 4 ++++ src/builds/builds.controller.ts | 6 ++--- src/builds/builds.service.spec.ts | 7 +++++- src/builds/builds.service.ts | 22 ++++++++----------- src/projects/projects.controller.spec.ts | 2 ++ src/projects/projects.controller.ts | 5 ++--- src/projects/projects.service.spec.ts | 10 ++++++++- src/projects/projects.service.ts | 6 ++--- src/shared/static/static.service.spec.ts | 8 ++++++- src/shared/static/static.service.ts | 21 +++++++++++++----- src/test-runs/test-runs.controller.spec.ts | 2 ++ src/test-runs/test-runs.controller.ts | 4 ++-- src/test-runs/test-runs.service.spec.ts | 6 ++++- src/test-runs/test-runs.service.ts | 8 +++---- .../test-variations.controller.spec.ts | 6 +++++ .../test-variations.controller.ts | 6 ++--- .../test-variations.service.spec.ts | 6 ++++- .../test-variations.service.ts | 6 ++--- src/test/test.controller.spec.ts | 10 +++++++++ src/test/test.controller.ts | 12 ++-------- src/test/test.service.spec.ts | 4 +++- src/test/test.service.ts | 6 ++--- src/users/users.controller.spec.ts | 2 ++ src/users/users.controller.ts | 4 ++-- src/users/users.service.spec.ts | 10 +++++++++ src/users/users.service.ts | 4 ++-- 29 files changed, 126 insertions(+), 68 deletions(-) diff --git a/package.json b/package.json index 86eed95f..24db1e3b 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json", - "docker:build_api": "docker build -t visualregressiontracker/api:$npm_package_version -f ./Dockerfile . && docker push visualregressiontracker/api:$npm_package_version", + "docker:build_api": "jest && docker build -t visualregressiontracker/api:$npm_package_version -f ./Dockerfile . && docker push visualregressiontracker/api:$npm_package_version", "docker:build_migration": "docker build -t visualregressiontracker/migration:0.0.2 -f ./prisma/Dockerfile ./prisma" }, "dependencies": { diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 5e8fd252..690be3a6 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -1,12 +1,11 @@ import { Module } from '@nestjs/common'; import { AuthService } from './auth.service'; -import { UsersModule } from '../users/users.module'; import { PassportModule } from '@nestjs/passport'; import { JwtModule } from '@nestjs/jwt'; import { JwtStrategy } from './jwt.strategy'; import { ApiGuard } from './guards/api.guard'; import { ConfigService } from '@nestjs/config'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; @Module({ imports: [ diff --git a/src/auth/guards/api.guard.ts b/src/auth/guards/api.guard.ts index 491291ff..929a3129 100644 --- a/src/auth/guards/api.guard.ts +++ b/src/auth/guards/api.guard.ts @@ -5,7 +5,7 @@ import { UnauthorizedException, } from '@nestjs/common'; import { Request } from 'express'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { PrismaService } from '../../prisma/prisma.service'; @Injectable() export class ApiGuard implements CanActivate { diff --git a/src/builds/builds.controller.spec.ts b/src/builds/builds.controller.spec.ts index 84b281dd..bfa907d6 100644 --- a/src/builds/builds.controller.spec.ts +++ b/src/builds/builds.controller.spec.ts @@ -1,5 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { BuildsController } from './builds.controller'; +import { BuildsService } from './builds.service'; +import { PrismaService } from '../prisma/prisma.service'; describe('Builds Controller', () => { let controller: BuildsController; @@ -7,6 +9,8 @@ describe('Builds Controller', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [BuildsController], + providers: [{ provide: BuildsService, useValue: {} }, + { provide: PrismaService, useValue: {} }], }).compile(); controller = module.get(BuildsController); diff --git a/src/builds/builds.controller.ts b/src/builds/builds.controller.ts index c4c9e62f..e52c63ee 100644 --- a/src/builds/builds.controller.ts +++ b/src/builds/builds.controller.ts @@ -10,11 +10,11 @@ import { Query, } from '@nestjs/common'; import { BuildsService } from './builds.service'; -import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; +import { JwtAuthGuard } from '../auth/guards/auth.guard'; import { ApiBearerAuth, ApiTags, ApiParam, ApiSecurity, ApiQuery } from '@nestjs/swagger'; import { CreateBuildDto } from './dto/build-create.dto'; -import { ApiGuard } from 'src/auth/guards/api.guard'; -import { Build, TestRun } from '@prisma/client'; +import { ApiGuard } from '../auth/guards/api.guard'; +import { Build } from '@prisma/client'; @Controller('builds') @ApiTags('builds') diff --git a/src/builds/builds.service.spec.ts b/src/builds/builds.service.spec.ts index a27e4603..e1ca7b8f 100644 --- a/src/builds/builds.service.spec.ts +++ b/src/builds/builds.service.spec.ts @@ -1,12 +1,17 @@ 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'; describe('BuildsService', () => { let service: BuildsService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [BuildsService], + providers: [BuildsService, + { provide: PrismaService, useValue: {} }, + { provide: TestRunsService, useValue: {} }, + ], }).compile(); service = module.get(BuildsService); diff --git a/src/builds/builds.service.ts b/src/builds/builds.service.ts index a281e768..d120cd15 100644 --- a/src/builds/builds.service.ts +++ b/src/builds/builds.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { CreateBuildDto } from './dto/build-create.dto'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { Build, TestRun } from '@prisma/client'; -import { TestRunsService } from 'src/test-runs/test-runs.service'; +import { PrismaService } from '../prisma/prisma.service'; +import { Build } from '@prisma/client'; +import { TestRunsService } from '../test-runs/test-runs.service'; @Injectable() export class BuildsService { @@ -11,15 +11,6 @@ export class BuildsService { private testRunsService: TestRunsService ) { } - async findOne(id: string): Promise { - return this.prismaService.build.findOne({ - where: { id }, - include: { - testRuns: true - } - }); - } - async findMany(projectId: string): Promise { return this.prismaService.build.findMany({ where: { projectId }, @@ -41,7 +32,12 @@ export class BuildsService { } async remove(id: string): Promise { - const build = await this.findOne(id); + const build = await this.prismaService.build.findOne({ + where: { id }, + include: { + testRuns: true + } + }); try { await Promise.all( diff --git a/src/projects/projects.controller.spec.ts b/src/projects/projects.controller.spec.ts index be8d7140..ce52c690 100644 --- a/src/projects/projects.controller.spec.ts +++ b/src/projects/projects.controller.spec.ts @@ -1,5 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ProjectsController } from './projects.controller'; +import { ProjectsService } from './projects.service'; describe('Projects Controller', () => { let controller: ProjectsController; @@ -7,6 +8,7 @@ describe('Projects Controller', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [ProjectsController], + providers: [{ provide: ProjectsService, useValue: {} }] }).compile(); controller = module.get(ProjectsController); diff --git a/src/projects/projects.controller.ts b/src/projects/projects.controller.ts index f9748b98..338c91af 100644 --- a/src/projects/projects.controller.ts +++ b/src/projects/projects.controller.ts @@ -9,11 +9,10 @@ import { Delete, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth, ApiOkResponse, ApiParam } from '@nestjs/swagger'; -import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; +import { JwtAuthGuard } from '../auth/guards/auth.guard'; import { ProjectsService } from './projects.service'; import { CreateProjectDto } from './dto/create-project.dto'; -import { Project, Build } from '@prisma/client'; -import { BuildsService } from 'src/builds/builds.service'; +import { Project } from '@prisma/client'; @Controller('projects') @ApiTags('projects') diff --git a/src/projects/projects.service.spec.ts b/src/projects/projects.service.spec.ts index d3b31018..58a847a8 100644 --- a/src/projects/projects.service.spec.ts +++ b/src/projects/projects.service.spec.ts @@ -1,12 +1,20 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ProjectsService } from './projects.service'; +import { PrismaService } from '../prisma/prisma.service'; +import { TestVariationsService } from '../test-variations/test-variations.service'; +import { BuildsService } from '../builds/builds.service'; describe('ProjectsService', () => { let service: ProjectsService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ProjectsService], + providers: [ + ProjectsService, + { provide: PrismaService, useValue: {} }, + { provide: TestVariationsService, useValue: {} }, + { provide: BuildsService, useValue: {} }, + ], }).compile(); service = module.get(ProjectsService); diff --git a/src/projects/projects.service.ts b/src/projects/projects.service.ts index 2ca4256e..8310272f 100644 --- a/src/projects/projects.service.ts +++ b/src/projects/projects.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { CreateProjectDto } from './dto/create-project.dto'; -import { BuildsService } from 'src/builds/builds.service'; -import { TestVariationsService } from 'src/test-variations/test-variations.service'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { BuildsService } from '../builds/builds.service'; +import { TestVariationsService } from '../test-variations/test-variations.service'; +import { PrismaService } from '../prisma/prisma.service'; import { Project } from '@prisma/client'; @Injectable() diff --git a/src/shared/static/static.service.spec.ts b/src/shared/static/static.service.spec.ts index ad49a5b6..1cdfc951 100644 --- a/src/shared/static/static.service.spec.ts +++ b/src/shared/static/static.service.spec.ts @@ -1,12 +1,18 @@ import { Test, TestingModule } from '@nestjs/testing'; import { StaticService } from './static.service'; +import { ConfigService } from '@nestjs/config'; describe('StaticService', () => { let service: StaticService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [StaticService], + providers: [ + StaticService, + { + provide: ConfigService, useValue: {} + } + ], }).compile(); service = module.get(StaticService); diff --git a/src/shared/static/static.service.ts b/src/shared/static/static.service.ts index bb340266..8f2954e4 100644 --- a/src/shared/static/static.service.ts +++ b/src/shared/static/static.service.ts @@ -1,15 +1,12 @@ import { Injectable } from '@nestjs/common'; -import { resolve } from 'path'; -import { writeFileSync, readFileSync, unlink, mkdir } from 'fs'; +import path from 'path'; +import { writeFileSync, readFileSync, unlink, mkdirSync, existsSync } from 'fs'; import { PNG, PNGWithMetadata } from 'pngjs'; import { ConfigService } from '@nestjs/config'; @Injectable() export class StaticService { constructor(private configService: ConfigService) { - mkdir(this.configService.get('IMG_UPLOAD_FOLDER'), { recursive: true }, (err) => { - if (err) throw err; - }); } saveImage(imageName: string, imageBuffer: Buffer) { @@ -32,6 +29,18 @@ export class StaticService { } private getImagePath(imageName: string): string { - return resolve(this.configService.get('IMG_UPLOAD_FOLDER'), imageName); + const dir = this.configService.get('IMG_UPLOAD_FOLDER') + this.ensureDirectoryExistence(dir) + return path.resolve(dir, imageName); + } + + private ensureDirectoryExistence(dir: string) { + const filePath = path.resolve(dir) + if (existsSync(filePath)) { + return true; + } else { + mkdirSync(dir, { recursive: true }); + this.ensureDirectoryExistence(dir) + } } } diff --git a/src/test-runs/test-runs.controller.spec.ts b/src/test-runs/test-runs.controller.spec.ts index a3d0569b..99ef17a3 100644 --- a/src/test-runs/test-runs.controller.spec.ts +++ b/src/test-runs/test-runs.controller.spec.ts @@ -1,5 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TestRunsController } from './test-runs.controller'; +import { TestRunsService } from './test-runs.service'; describe('TestRuns Controller', () => { let controller: TestRunsController; @@ -7,6 +8,7 @@ describe('TestRuns Controller', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [TestRunsController], + providers: [{ provide: TestRunsService, useValue: {} }] }).compile(); controller = module.get(TestRunsController); diff --git a/src/test-runs/test-runs.controller.ts b/src/test-runs/test-runs.controller.ts index e6a27bc8..29e29903 100644 --- a/src/test-runs/test-runs.controller.ts +++ b/src/test-runs/test-runs.controller.ts @@ -1,9 +1,9 @@ import { Controller, Delete, UseGuards, Param, ParseUUIDPipe, Put, Body, Get, Query } from '@nestjs/common'; import { ApiTags, ApiParam, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; -import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; +import { JwtAuthGuard } from '../auth/guards/auth.guard'; import { TestRun } from '@prisma/client'; import { TestRunsService } from './test-runs.service'; -import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; +import { IgnoreAreaDto } from '../test/dto/ignore-area.dto'; @ApiTags('test-runs') @Controller('test-runs') diff --git a/src/test-runs/test-runs.service.spec.ts b/src/test-runs/test-runs.service.spec.ts index c370426d..34d6d82c 100644 --- a/src/test-runs/test-runs.service.spec.ts +++ b/src/test-runs/test-runs.service.spec.ts @@ -1,12 +1,16 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TestRunsService } from './test-runs.service'; +import { PrismaService } from '../prisma/prisma.service'; +import { StaticService } from '../shared/static/static.service'; describe('TestRunsService', () => { let service: TestRunsService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [TestRunsService], + providers: [TestRunsService, + { provide: PrismaService, useValue: {} }, + { provide: StaticService, useValue: {} },], }).compile(); service = module.get(TestRunsService); diff --git a/src/test-runs/test-runs.service.ts b/src/test-runs/test-runs.service.ts index e0439e9d..1bb980cc 100644 --- a/src/test-runs/test-runs.service.ts +++ b/src/test-runs/test-runs.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@nestjs/common'; import { PNG, PNGWithMetadata } from 'pngjs'; import Pixelmatch from 'pixelmatch'; -import { CreateTestRequestDto } from 'src/test/dto/create-test-request.dto'; -import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; -import { StaticService } from 'src/shared/static/static.service'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { CreateTestRequestDto } from '../test/dto/create-test-request.dto'; +import { IgnoreAreaDto } from '../test/dto/ignore-area.dto'; +import { StaticService } from '../shared/static/static.service'; +import { PrismaService } from '../prisma/prisma.service'; import { TestRun, TestStatus, TestVariation, TestRunCreateInput } from '@prisma/client'; @Injectable() diff --git a/src/test-variations/test-variations.controller.spec.ts b/src/test-variations/test-variations.controller.spec.ts index 961d6372..74fc4073 100644 --- a/src/test-variations/test-variations.controller.spec.ts +++ b/src/test-variations/test-variations.controller.spec.ts @@ -1,5 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TestVariationsController } from './test-variations.controller'; +import { TestVariationsService } from './test-variations.service'; +import { PrismaService } from '../prisma/prisma.service'; describe('TestVariations Controller', () => { let controller: TestVariationsController; @@ -7,6 +9,10 @@ describe('TestVariations Controller', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [TestVariationsController], + providers: [ + { provide: TestVariationsService, useValue: {} }, + { provide: PrismaService, useValue: {} }, + ], }).compile(); controller = module.get(TestVariationsController); diff --git a/src/test-variations/test-variations.controller.ts b/src/test-variations/test-variations.controller.ts index 9b9c29b1..f97f5280 100644 --- a/src/test-variations/test-variations.controller.ts +++ b/src/test-variations/test-variations.controller.ts @@ -2,9 +2,9 @@ import { Controller, ParseUUIDPipe, Get, UseGuards, Param, Query, Put, Body } fr import { ApiTags, ApiParam, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; import { TestVariationsService } from './test-variations.service'; import { TestVariation } from '@prisma/client'; -import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; +import { JwtAuthGuard } from '../auth/guards/auth.guard'; +import { PrismaService } from '../prisma/prisma.service'; +import { IgnoreAreaDto } from '../test/dto/ignore-area.dto'; @ApiTags('test-variations') @Controller('test-variations') diff --git a/src/test-variations/test-variations.service.spec.ts b/src/test-variations/test-variations.service.spec.ts index 60532210..931abfc0 100644 --- a/src/test-variations/test-variations.service.spec.ts +++ b/src/test-variations/test-variations.service.spec.ts @@ -1,12 +1,16 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TestVariationsService } from './test-variations.service'; +import { PrismaService } from '../prisma/prisma.service'; describe('TestVariationsService', () => { let service: TestVariationsService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [TestVariationsService], + providers: [ + TestVariationsService, + { provide: PrismaService, useValue: {} }, + ], }).compile(); service = module.get(TestVariationsService); diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index b602d796..44060d9e 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; -import { CreateTestRequestDto } from 'src/test/dto/create-test-request.dto'; -import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { CreateTestRequestDto } from '../test/dto/create-test-request.dto'; +import { IgnoreAreaDto } from '../test/dto/ignore-area.dto'; +import { PrismaService } from '../prisma/prisma.service'; import { TestVariation } from '@prisma/client'; @Injectable() diff --git a/src/test/test.controller.spec.ts b/src/test/test.controller.spec.ts index 11e5e26d..ef9dafa7 100644 --- a/src/test/test.controller.spec.ts +++ b/src/test/test.controller.spec.ts @@ -1,5 +1,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TestController } from './test.controller'; +import { TestService } from './test.service'; +import { TestVariationsService } from '../test-variations/test-variations.service'; +import { TestRunsService } from '../test-runs/test-runs.service'; +import { PrismaService } from '../prisma/prisma.service'; describe('Test Controller', () => { let controller: TestController; @@ -7,6 +11,12 @@ describe('Test Controller', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [TestController], + providers: [ + { provide: PrismaService, useValue: {} }, + { provide: TestService, useValue: {} }, + { provide: TestVariationsService, useValue: {} }, + { provide: TestRunsService, useValue: {} }, + ], }).compile(); controller = module.get(TestController); diff --git a/src/test/test.controller.ts b/src/test/test.controller.ts index cce95cbd..1ffcf3bc 100644 --- a/src/test/test.controller.ts +++ b/src/test/test.controller.ts @@ -1,21 +1,13 @@ import { Controller, - Get, UseGuards, - Param, - ParseUUIDPipe, Post, Body, - Put, - Delete, } from '@nestjs/common'; -import { ApiTags, ApiParam, ApiOkResponse, ApiBearerAuth, ApiSecurity } from '@nestjs/swagger'; -import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; +import { ApiTags, ApiOkResponse, ApiSecurity } from '@nestjs/swagger'; import { TestService } from './test.service'; import { CreateTestRequestDto } from './dto/create-test-request.dto'; -import { ApiGuard } from 'src/auth/guards/api.guard'; -import { IgnoreAreaDto } from './dto/ignore-area.dto'; -import { TestRun, TestVariation } from '@prisma/client'; +import { ApiGuard } from '../auth/guards/api.guard'; import { TestRunResultDto } from './dto/testRunResult.dto'; @ApiTags('test') diff --git a/src/test/test.service.spec.ts b/src/test/test.service.spec.ts index 1aacbe5d..05b7f0f9 100644 --- a/src/test/test.service.spec.ts +++ b/src/test/test.service.spec.ts @@ -1,12 +1,14 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TestService } from './test.service'; +import { TestRunsService } from '../test-runs/test-runs.service'; +import { TestVariationsService } from '../test-variations/test-variations.service'; describe('TestService', () => { let service: TestService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [TestService], + providers: [TestService, { provide: TestRunsService, useValue: {} }, { provide: TestVariationsService, useValue: {} },], }).compile(); service = module.get(TestService); diff --git a/src/test/test.service.ts b/src/test/test.service.ts index f7e40b6d..1568172c 100644 --- a/src/test/test.service.ts +++ b/src/test/test.service.ts @@ -1,9 +1,7 @@ import { Injectable } from '@nestjs/common'; -import { TestVariationsService } from 'src/test-variations/test-variations.service'; -import { TestRunsService } from 'src/test-runs/test-runs.service'; +import { TestVariationsService } from '../test-variations/test-variations.service'; +import { TestRunsService } from '../test-runs/test-runs.service'; import { CreateTestRequestDto } from './dto/create-test-request.dto'; -import { IgnoreAreaDto } from './dto/ignore-area.dto'; -import { TestRun, TestVariation } from '@prisma/client'; import { TestRunResultDto } from './dto/testRunResult.dto'; @Injectable() diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts index 5bd21bae..dc032175 100644 --- a/src/users/users.controller.spec.ts +++ b/src/users/users.controller.spec.ts @@ -1,5 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UsersController } from './users.controller'; +import { UsersService } from './users.service'; describe('Users Controller', () => { let controller: UsersController; @@ -7,6 +8,7 @@ describe('Users Controller', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [UsersController], + providers: [{ provide: UsersService, useValue: {} }] }).compile(); controller = module.get(UsersController); diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index d11c6e4c..98e40f94 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -1,9 +1,9 @@ -import { Controller, Post, Body, Get, UseGuards, Param, ParseUUIDPipe, Put, Request } from '@nestjs/common'; +import { Controller, Post, Body, Get, UseGuards, Param, ParseUUIDPipe, Put } from '@nestjs/common'; import { UsersService } from './users.service'; import { ApiOkResponse, ApiParam, ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { UserLoginResponseDto } from './dto/user-login-response.dto'; import { CreateUserDto } from './dto/user-create.dto'; -import { JwtAuthGuard } from 'src/auth/guards/auth.guard'; +import { JwtAuthGuard } from '../auth/guards/auth.guard'; import { UserDto } from './dto/user.dto'; import { UpdateUserDto } from './dto/user-update.dto'; import { UserLoginRequestDto } from './dto/user-login-request.dto'; diff --git a/src/users/users.service.spec.ts b/src/users/users.service.spec.ts index 65a7ef35..de28b0ba 100644 --- a/src/users/users.service.spec.ts +++ b/src/users/users.service.spec.ts @@ -1,5 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UsersService } from './users.service'; +import { AuthService } from '../auth/auth.service'; +import { PrismaService } from '../prisma/prisma.service'; describe('UsersService', () => { let service: UsersService; @@ -8,6 +10,14 @@ describe('UsersService', () => { const module: TestingModule = await Test.createTestingModule({ providers: [ UsersService, + { + provide: PrismaService, useValue: { + user: { + findMany: jest.fn().mockResolvedValueOnce([]) + } + } + }, + { provide: AuthService, useValue: {} }, ], }).compile(); diff --git a/src/users/users.service.ts b/src/users/users.service.ts index c3366fee..142f7b70 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -1,11 +1,11 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { CreateUserDto } from './dto/user-create.dto'; import { UserLoginResponseDto } from './dto/user-login-response.dto'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; import { User } from '@prisma/client'; import { UserDto } from './dto/user.dto'; import { UpdateUserDto } from './dto/user-update.dto'; -import { AuthService } from 'src/auth/auth.service'; +import { AuthService } from '../auth/auth.service'; import { UserLoginRequestDto } from './dto/user-login-request.dto'; @Injectable() From 5d800f4f1af9ea0a92bc933ee3fc07c6e198d721 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 23 May 2020 22:19:45 +0200 Subject: [PATCH 08/16] users. e2e tests added --- package-lock.json | 12 ++ package.json | 4 +- src/auth/auth.service.ts | 4 +- src/auth/jwt.strategy.ts | 2 +- src/builds/builds.module.ts | 6 +- src/projects/projects.module.ts | 6 +- src/test-runs/test-runs.module.ts | 4 +- src/test-variations/test-variations.module.ts | 3 +- src/test/test.module.ts | 8 +- src/users/users.controller.ts | 18 +-- src/users/users.module.ts | 4 +- src/users/users.service.ts | 6 +- test/app.e2e-spec.ts | 24 ---- test/preconditions.ts | 30 +++++ test/users.e2e-spec.ts | 127 ++++++++++++++++++ 15 files changed, 199 insertions(+), 59 deletions(-) delete mode 100644 test/app.e2e-spec.ts create mode 100644 test/preconditions.ts create mode 100644 test/users.e2e-spec.ts diff --git a/package-lock.json b/package-lock.json index 55bdde16..52e79a11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1377,6 +1377,12 @@ "@babel/types": "^7.3.0" } }, + "@types/bcryptjs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", + "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==", + "dev": true + }, "@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -1643,6 +1649,12 @@ } } }, + "@types/uuid-apikey": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@types/uuid-apikey/-/uuid-apikey-1.4.0.tgz", + "integrity": "sha512-fMUfWcgNH/PFVjkoMJXEFK52vwsifNqDwJ9CmfGTHkKSSbFpsgqa4IKbm/Cgi+dVtOX+6mhIgkEOqsDO1VMnyQ==", + "dev": true + }, "@types/webpack": { "version": "4.41.8", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.8.tgz", diff --git a/package.json b/package.json index 24db1e3b..8a216d40 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json", - "docker:build_api": "jest && docker build -t visualregressiontracker/api:$npm_package_version -f ./Dockerfile . && docker push visualregressiontracker/api:$npm_package_version", + "docker:build_api": "npm run test && npm run test:e2e && docker build -t visualregressiontracker/api:$npm_package_version -f ./Dockerfile . && docker push visualregressiontracker/api:$npm_package_version", "docker:build_migration": "docker build -t visualregressiontracker/migration:0.0.2 -f ./prisma/Dockerfile ./prisma" }, "dependencies": { @@ -53,6 +53,7 @@ "@nestjs/schematics": "^7.0.0", "@nestjs/testing": "^7.0.0", "@prisma/cli": "^2.0.0-beta.5", + "@types/bcryptjs": "^2.4.2", "@types/express": "^4.17.3", "@types/jest": "25.1.4", "@types/node": "^13.9.1", @@ -61,6 +62,7 @@ "@types/pixelmatch": "^5.1.0", "@types/pngjs": "^3.4.2", "@types/supertest": "^2.0.8", + "@types/uuid-apikey": "^1.4.0", "@typescript-eslint/eslint-plugin": "^2.23.0", "@typescript-eslint/parser": "^2.23.0", "eslint": "^6.8.0", diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 5dd584ae..bb8e0bdb 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -20,8 +20,8 @@ export class AuthService { return uuidAPIKey.create({ noDashes: true }).apiKey; } - async compare(password1: string, password2: string): Promise { - return await compare(password1, password2); + async compare(password: string, hashedPassword: string): Promise { + return await compare(password, hashedPassword); } signToken(user: User): string { diff --git a/src/auth/jwt.strategy.ts b/src/auth/jwt.strategy.ts index 4a0a8a68..c0c73973 100644 --- a/src/auth/jwt.strategy.ts +++ b/src/auth/jwt.strategy.ts @@ -3,7 +3,7 @@ import { PassportStrategy } from '@nestjs/passport'; import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { JwtPayload } from './jwt-payload.model'; import { ConfigService } from '@nestjs/config'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { diff --git a/src/builds/builds.module.ts b/src/builds/builds.module.ts index 67ac98d2..504805bd 100644 --- a/src/builds/builds.module.ts +++ b/src/builds/builds.module.ts @@ -1,9 +1,9 @@ import { Module } from '@nestjs/common'; import { BuildsService } from './builds.service'; import { BuildsController } from './builds.controller'; -import { UsersModule } from 'src/users/users.module'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { TestRunsModule } from 'src/test-runs/test-runs.module'; +import { UsersModule } from '../users/users.module'; +import { PrismaService } from '../prisma/prisma.service'; +import { TestRunsModule } from '../test-runs/test-runs.module'; @Module({ imports: [UsersModule, TestRunsModule], diff --git a/src/projects/projects.module.ts b/src/projects/projects.module.ts index a5e0e08f..c47fe15e 100644 --- a/src/projects/projects.module.ts +++ b/src/projects/projects.module.ts @@ -1,9 +1,9 @@ import { Module } from '@nestjs/common'; import { ProjectsService } from './projects.service'; import { ProjectsController } from './projects.controller'; -import { BuildsModule } from 'src/builds/builds.module'; -import { TestVariationsModule } from 'src/test-variations/test-variations.module'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { BuildsModule } from '../builds/builds.module'; +import { TestVariationsModule } from '../test-variations/test-variations.module'; +import { PrismaService } from '../prisma/prisma.service'; @Module({ imports: [BuildsModule, TestVariationsModule], diff --git a/src/test-runs/test-runs.module.ts b/src/test-runs/test-runs.module.ts index 5d48c188..453b88b0 100644 --- a/src/test-runs/test-runs.module.ts +++ b/src/test-runs/test-runs.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { TestRunsService } from './test-runs.service'; -import { SharedModule } from 'src/shared/shared.module'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { SharedModule } from '../shared/shared.module'; +import { PrismaService } from '../prisma/prisma.service'; import { TestRunsController } from './test-runs.controller'; @Module({ diff --git a/src/test-variations/test-variations.module.ts b/src/test-variations/test-variations.module.ts index 7c680a67..337832e8 100644 --- a/src/test-variations/test-variations.module.ts +++ b/src/test-variations/test-variations.module.ts @@ -1,8 +1,7 @@ import { Module } from '@nestjs/common'; import { TestVariationsService } from './test-variations.service'; import { TestVariationsController } from './test-variations.controller'; -import { TestRunsModule } from 'src/test-runs/test-runs.module'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; @Module({ providers: [TestVariationsService, PrismaService], diff --git a/src/test/test.module.ts b/src/test/test.module.ts index 34f483e6..b80767ba 100644 --- a/src/test/test.module.ts +++ b/src/test/test.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { TestService } from './test.service'; import { TestController } from './test.controller'; -import { TestRunsModule } from 'src/test-runs/test-runs.module'; -import { TestVariationsModule } from 'src/test-variations/test-variations.module'; -import { UsersModule } from 'src/users/users.module'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { TestRunsModule } from '../test-runs/test-runs.module'; +import { TestVariationsModule } from '../test-variations/test-variations.module'; +import { UsersModule } from '../users/users.module'; +import { PrismaService } from '../prisma/prisma.service'; @Module({ imports: [TestRunsModule, TestVariationsModule, UsersModule], diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 98e40f94..cc2ddc29 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -49,24 +49,14 @@ export class UsersController { return this.usersService.changePassword(user, password) } - @Get(':id') - @ApiParam({ name: 'id', required: true }) - @ApiOkResponse({ type: UserDto }) - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - get(@Param('id', new ParseUUIDPipe()) id: string): Promise { - return this.usersService.get(id); - } - - @Put(':id') - @ApiParam({ name: 'id', required: true }) + @Put() @ApiOkResponse({ type: UserLoginResponseDto }) @ApiBearerAuth() @UseGuards(JwtAuthGuard) - updated( - @Param('id', new ParseUUIDPipe()) id: string, + update( + @CurrentUser() user: User, @Body() updateUserDto: UpdateUserDto ): Promise { - return this.usersService.update(id, updateUserDto); + return this.usersService.update(user.id, updateUserDto); } } diff --git a/src/users/users.module.ts b/src/users/users.module.ts index 462844ee..7d54196b 100644 --- a/src/users/users.module.ts +++ b/src/users/users.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { UsersService } from './users.service'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; import { UsersController } from './users.controller'; -import { AuthModule } from 'src/auth/auth.module'; +import { AuthModule } from '../auth/auth.module'; @Module({ imports: [AuthModule], diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 142f7b70..4eb31726 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -67,8 +67,12 @@ export class UsersService { } } + async findOne(id: string): Promise { + return this.prismaService.user.findOne({ where: { id } }) + } + async get(id: string): Promise { - const user = await this.prismaService.user.findOne({ where: { id } }) + const user = await this.findOne(id) return new UserDto(user) } diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts deleted file mode 100644 index 50cda623..00000000 --- a/test/app.e2e-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); -}); diff --git a/test/preconditions.ts b/test/preconditions.ts new file mode 100644 index 00000000..3aacb8f2 --- /dev/null +++ b/test/preconditions.ts @@ -0,0 +1,30 @@ +import { INestApplication } from '@nestjs/common'; +import { UsersService } from 'src/users/users.service'; +import { UserLoginRequestDto } from 'src/users/dto/user-login-request.dto'; +import uuidAPIKey from 'uuid-apikey'; +import request, { Test } from 'supertest'; +import { CreateUserDto } from 'src/users/dto/user-create.dto'; + +export const generateUser = (password: string = '123456'): { email: string, password: string, firstName: string, lastName: string } => ({ + email: `${uuidAPIKey.create().uuid}@example.com'`, + password, + firstName: 'fName', + lastName: 'lName', +}) + +export const requestWithAuth = (app: INestApplication, method: 'post' | 'get' | 'put' | 'delete', url: string, body = {}, token: string): Test => + request(app.getHttpServer()) + [method](url) + .set('Authorization', 'Bearer ' + token) + .send(body) + +// export const haveUserCreated = (usersService: UsersService, password: string = '123456') => { +// return usersService.create({ +// email: `${uuidAPIKey.create().uuid}@example.com'`, +// password, +// firstName: 'fName', +// lastName: 'lName', +// }) +// } + +// export const haveUserLogged = (usersService: UsersService, user: UserLoginRequestDto) => usersService.login(user) \ No newline at end of file diff --git a/test/users.e2e-spec.ts b/test/users.e2e-spec.ts new file mode 100644 index 00000000..9342323a --- /dev/null +++ b/test/users.e2e-spec.ts @@ -0,0 +1,127 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from '../src/app.module'; +import uuidAPIKey from 'uuid-apikey'; +import { UsersService } from '../src/users/users.service'; +import { CreateUserDto } from '../src/users/dto/user-create.dto'; +import { UserLoginRequestDto } from '../src/users/dto/user-login-request.dto'; +import { compareSync } from 'bcryptjs'; +import { requestWithAuth, generateUser } from './preconditions'; + +describe('Users (e2e)', () => { + let app: INestApplication; + let usersService: UsersService; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + usersService = moduleFixture.get(UsersService) + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('POST /register', () => { + const user: CreateUserDto = { + email: `${uuidAPIKey.create().uuid}@example.com'`, + password: '123456', + firstName: 'fName', + lastName: 'lName', + } + return request(app.getHttpServer()) + .post('/users/register') + .send(user) + .expect(201) + .expect(res => { + expect(res.body.email).toBe(user.email); + expect(res.body.firstName).toBe(user.firstName); + expect(res.body.lastName).toBe(user.lastName); + expect(res.body.apiKey).not.toBeNull(); + }) + }); + + it('POST /login', async () => { + const password = '123456' + const user = await usersService.create(generateUser(password)) + const loginData: UserLoginRequestDto = { + email: user.email, + password + } + + return request(app.getHttpServer()) + .post('/users/login') + .send(loginData) + .expect(201) + .expect(res => { + expect(res.body.id).toBe(user.id); + expect(res.body.email).toBe(user.email); + expect(res.body.firstName).toBe(user.firstName); + expect(res.body.lastName).toBe(user.lastName); + expect(res.body.apiKey).toBe(user.apiKey); + expect(res.body.token).not.toBeNull(); + }) + }); + + it('GET /newApiKey', async () => { + const password = '123456' + const user = await usersService.create(generateUser(password)) + const loggedUser = await usersService.login({ + email: user.email, + password + }) + + const res = await requestWithAuth(app, 'get', '/users/newApiKey', {}, loggedUser.token) + .expect(200) + + const newUser = await usersService.findOne(user.id) + expect(res.text).not.toBe(user.apiKey); + expect(res.text).toBe(newUser.apiKey); + }); + + it('PUT /password', async () => { + const newPassword = 'newPassword' + const password = '123456' + const user = await usersService.create(generateUser(password)) + const loggedUser = await usersService.login({ + email: user.email, + password + }) + + await requestWithAuth(app, 'put', '/users/password', { password: newPassword }, loggedUser.token) + .expect(200) + + const newUser = await usersService.findOne(user.id) + expect(compareSync(newPassword, newUser.password)).toBe(true); + }); + + it('PUT /', async () => { + const password = '123456' + const user = await usersService.create(generateUser(password)) + const editedUser = { + email: `${uuidAPIKey.create().uuid}@example.com'`, + firstName: 'EDITEDfName', + lastName: 'EDITEDlName', + } + + const loggedUser = await usersService.login({ + email: user.email, + password + }) + + const res = await requestWithAuth(app, 'put', '/users', editedUser, loggedUser.token) + .expect(200) + + expect(res.body.id).toBe(user.id); + expect(res.body.email).toBe(editedUser.email); + expect(res.body.firstName).toBe(editedUser.firstName); + expect(res.body.lastName).toBe(editedUser.lastName); + expect(res.body.apiKey).toBe(user.apiKey); + expect(res.body.token).not.toBeNull(); + }); +}); From d45c8452f8d067a7c4117ffcdda1ad0116dcca37 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sat, 23 May 2020 23:40:25 +0200 Subject: [PATCH 09/16] projects. e2e tests added --- test/preconditions.ts | 21 +++++---- test/projects.e2e-spec.ts | 93 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 test/projects.e2e-spec.ts diff --git a/test/preconditions.ts b/test/preconditions.ts index 3aacb8f2..1ff5b8a9 100644 --- a/test/preconditions.ts +++ b/test/preconditions.ts @@ -2,10 +2,10 @@ import { INestApplication } from '@nestjs/common'; import { UsersService } from 'src/users/users.service'; import { UserLoginRequestDto } from 'src/users/dto/user-login-request.dto'; import uuidAPIKey from 'uuid-apikey'; -import request, { Test } from 'supertest'; +import request, { Test, CallbackHandler, Request } from 'supertest'; import { CreateUserDto } from 'src/users/dto/user-create.dto'; -export const generateUser = (password: string = '123456'): { email: string, password: string, firstName: string, lastName: string } => ({ +export const generateUser = (password: string): { email: string, password: string, firstName: string, lastName: string } => ({ email: `${uuidAPIKey.create().uuid}@example.com'`, password, firstName: 'fName', @@ -18,13 +18,12 @@ export const requestWithAuth = (app: INestApplication, method: 'post' | 'get' | .set('Authorization', 'Bearer ' + token) .send(body) -// export const haveUserCreated = (usersService: UsersService, password: string = '123456') => { -// return usersService.create({ -// email: `${uuidAPIKey.create().uuid}@example.com'`, -// password, -// firstName: 'fName', -// lastName: 'lName', -// }) -// } +export const haveUserLogged = async (usersService: UsersService) => { + const password = '123456' + const user = await usersService.create(generateUser(password)) -// export const haveUserLogged = (usersService: UsersService, user: UserLoginRequestDto) => usersService.login(user) \ No newline at end of file + return usersService.login({ + email: user.email, + password + }) +} \ No newline at end of file diff --git a/test/projects.e2e-spec.ts b/test/projects.e2e-spec.ts new file mode 100644 index 00000000..d420bc22 --- /dev/null +++ b/test/projects.e2e-spec.ts @@ -0,0 +1,93 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { AppModule } from '../src/app.module'; +import { UsersService } from '../src/users/users.service'; +import { requestWithAuth, haveUserLogged } from './preconditions'; +import { ProjectsService } from '../src/projects/projects.service'; +import uuidAPIKey from 'uuid-apikey'; + +const project = { + id: uuidAPIKey.create().uuid, + name: 'Test project' +} + +const projectServiceMock = { + findAll: () => ['test'], + create: () => project, + remove: () => project, +}; + +describe('Projects (e2e)', () => { + let app: INestApplication; + let usersService: UsersService; + let loggedUser + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(ProjectsService) + .useValue(projectServiceMock) + .compile(); + + app = moduleFixture.createNestApplication(); + usersService = moduleFixture.get(UsersService) + + await app.init(); + loggedUser = await haveUserLogged(usersService) + }); + + afterAll(async () => { + await app.close(); + }); + + describe('POST /', () => { + it('200', () => { + return requestWithAuth(app, 'post', '/projects', project, loggedUser.token) + .expect(201) + .expect(res => { + expect(res.body.name).toBe(project.name); + }) + }); + + it('401', () => { + return requestWithAuth(app, 'post', '/projects', project, '') + .expect(401) + }); + }) + + describe('GET /', () => { + it('200', async () => { + const res = await requestWithAuth(app, 'get', '/projects', {}, loggedUser.token) + .expect(200) + + expect(res.body).toEqual(expect.arrayContaining(projectServiceMock.findAll())) + }); + + it('401', async () => { + await requestWithAuth(app, 'get', '/projects', {}, '') + .expect(401) + }); + }) + + + describe('DELETE /', () => { + + it('can delete', async () => { + const res = await requestWithAuth(app, 'delete', `/projects/${project.id}`, {}, loggedUser.token) + .expect(200) + + expect(res.body).toStrictEqual(projectServiceMock.remove()) + }) + + it('not valid UUID', async () => { + await requestWithAuth(app, 'delete', `/projects/123`, {}, loggedUser.token) + .expect(400) + }) + + it('not valid token', async () => { + await requestWithAuth(app, 'delete', `/projects/${project.id}`, {}, 'asd') + .expect(401) + }) + }) +}); From 603e1a8d5f0adf455a38ca807bcec63a3617f3f4 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sun, 24 May 2020 13:05:12 +0200 Subject: [PATCH 10/16] baseline history populated on approve --- .vscode/launch.json | 10 +- .../20200524122552-baseline-history/README.md | 82 +++++++ .../schema.prisma | 105 ++++++++ .../steps.json | 229 ++++++++++++++++++ .../migrations/20200524123205-test/README.md | 40 +++ .../20200524123205-test/schema.prisma | 105 ++++++++ .../migrations/20200524123205-test/steps.json | 11 + prisma/migrations/migrate.lock | 6 +- prisma/schema.prisma | 24 +- src/test-runs/test-runs.service.ts | 17 ++ 10 files changed, 618 insertions(+), 11 deletions(-) create mode 100644 prisma/migrations/20200524122552-baseline-history/README.md create mode 100644 prisma/migrations/20200524122552-baseline-history/schema.prisma create mode 100644 prisma/migrations/20200524122552-baseline-history/steps.json create mode 100644 prisma/migrations/20200524123205-test/README.md create mode 100644 prisma/migrations/20200524123205-test/schema.prisma create mode 100644 prisma/migrations/20200524123205-test/steps.json diff --git a/.vscode/launch.json b/.vscode/launch.json index dc6a3be0..92a15d15 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,14 +4,14 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - - { "type": "node", "request": "attach", - "name": "Attach by Process ID", + "name": "Attach NestJS WS", "port": 9229, - "processId": "${command:PickProcess}" - }, + "restart": true, + "stopOnEntry": false, + "protocol": "inspector" + } ] } \ No newline at end of file diff --git a/prisma/migrations/20200524122552-baseline-history/README.md b/prisma/migrations/20200524122552-baseline-history/README.md new file mode 100644 index 00000000..7c9e546e --- /dev/null +++ b/prisma/migrations/20200524122552-baseline-history/README.md @@ -0,0 +1,82 @@ +# Migration `20200524122552-baseline-history` + +This migration has been generated by Pavel Strunkin at 5/24/2020, 12:25:52 PM. +You can check out the [state of the schema](./schema.prisma) after the migration. + +## Database Steps + +```sql +CREATE TABLE "public"."Baseline" ( +"baselineName" text NOT NULL ,"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"id" text NOT NULL ,"testRunId" text NOT NULL ,"testVariationId" text NOT NULL ,"updatedAt" timestamp(3) NOT NULL , + PRIMARY KEY ("id")) + +CREATE UNIQUE INDEX "Baseline_testRunId" ON "public"."Baseline"("testRunId") + +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 CASCADE ON UPDATE CASCADE +``` + +## Changes + +```diff +diff --git schema.prisma schema.prisma +migration 20200523130656-test-run-owns-variation-data..20200524122552-baseline-history +--- datamodel.dml ++++ datamodel.dml +@@ -3,9 +3,9 @@ + } + datasource db { + provider = "postgresql" +- url = "***" ++ url = env("DATABASE_URL") + } + model Build { + id String @default(uuid()) @id +@@ -51,26 +51,40 @@ + os String? + viewport String? + baselineName String? + ignoreAreas String @default("[]") ++ // Baseline ++ baseline Baseline + } + model TestVariation { +- id String @default(uuid()) @id ++ id String @default(uuid()) @id + name String + browser String? + device String? + os String? + viewport String? + baselineName String? +- ignoreAreas String @default("[]") ++ ignoreAreas String @default("[]") + projectId String +- project Project @relation(fields: [projectId], references: [id]) ++ project Project @relation(fields: [projectId], references: [id]) + testRuns TestRun[] +- updatedAt DateTime @updatedAt +- createdAt DateTime @default(now()) ++ baselines Baseline[] ++ updatedAt DateTime @updatedAt ++ createdAt DateTime @default(now()) + } ++model Baseline { ++ id String @default(uuid()) @id ++ baselineName String ++ updatedAt DateTime @updatedAt ++ createdAt DateTime @default(now()) ++ testVariation TestVariation @relation(fields: [testVariationId], references: [id]) ++ testVariationId String ++ testRunId String ++ testRun TestRun @relation(fields: [testRunId], references: [id]) ++} ++ + model User { + id String @default(uuid()) @id + email String @unique + password String +``` + + diff --git a/prisma/migrations/20200524122552-baseline-history/schema.prisma b/prisma/migrations/20200524122552-baseline-history/schema.prisma new file mode 100644 index 00000000..30648fad --- /dev/null +++ b/prisma/migrations/20200524122552-baseline-history/schema.prisma @@ -0,0 +1,105 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = "***" +} + +model Build { + id String @default(uuid()) @id + number Int? + branchName String? + status String? + testRuns TestRun[] + projectId String + project Project @relation(fields: [projectId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + user User? @relation(fields: [userId], references: [id]) + userId String? +} + +model Project { + id String @default(uuid()) @id + name String + builds Build[] + testVariations TestVariation[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +model TestRun { + id String @default(uuid()) @id + imageName String + diffName String? + diffPercent Float? + diffTollerancePercent Float @default(1.0) + pixelMisMatchCount Int? + status TestStatus + buildId String + build Build @relation(fields: [buildId], references: [id]) + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + // Test variation data + name String @default("") + browser String? + device String? + os String? + viewport String? + baselineName String? + ignoreAreas String @default("[]") + // Baseline + baseline Baseline +} + +model TestVariation { + id String @default(uuid()) @id + name String + browser String? + device String? + os String? + viewport String? + baselineName String? + ignoreAreas String @default("[]") + projectId String + project Project @relation(fields: [projectId], references: [id]) + testRuns TestRun[] + baselines Baseline[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +model Baseline { + id String @default(uuid()) @id + baselineName String + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + testVariationId String + testRunId String + testRun TestRun @relation(fields: [testRunId], references: [id]) +} + +model User { + id String @default(uuid()) @id + email String @unique + password String + firstName String? + lastName String? + apiKey String @unique + isActive Boolean @default(true) + builds Build[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +enum TestStatus { + failed + new + ok + unresolved +} \ No newline at end of file diff --git a/prisma/migrations/20200524122552-baseline-history/steps.json b/prisma/migrations/20200524122552-baseline-history/steps.json new file mode 100644 index 00000000..1b6b836b --- /dev/null +++ b/prisma/migrations/20200524122552-baseline-history/steps.json @@ -0,0 +1,229 @@ +{ + "version": "0.3.14-fixed", + "steps": [ + { + "tag": "CreateModel", + "model": "Baseline" + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "id", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "Baseline", + "field": "id" + }, + "directive": "default" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "id" + }, + "directive": "default" + }, + "argument": "", + "value": "uuid()" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "Baseline", + "field": "id" + }, + "directive": "id" + } + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "baselineName", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "updatedAt", + "type": "DateTime", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "Baseline", + "field": "updatedAt" + }, + "directive": "updatedAt" + } + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "createdAt", + "type": "DateTime", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "Baseline", + "field": "createdAt" + }, + "directive": "default" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "createdAt" + }, + "directive": "default" + }, + "argument": "", + "value": "now()" + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "testVariation", + "type": "TestVariation", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testVariation" + }, + "directive": "relation" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testVariation" + }, + "directive": "relation" + }, + "argument": "fields", + "value": "[testVariationId]" + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testVariation" + }, + "directive": "relation" + }, + "argument": "references", + "value": "[id]" + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "testVariationId", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "testRunId", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "testRun", + "type": "TestRun", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testRun" + }, + "directive": "relation" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testRun" + }, + "directive": "relation" + }, + "argument": "fields", + "value": "[testRunId]" + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testRun" + }, + "directive": "relation" + }, + "argument": "references", + "value": "[id]" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "baseline", + "type": "Baseline", + "arity": "Required" + }, + { + "tag": "CreateField", + "model": "TestVariation", + "field": "baselines", + "type": "Baseline", + "arity": "List" + } + ] +} \ No newline at end of file diff --git a/prisma/migrations/20200524123205-test/README.md b/prisma/migrations/20200524123205-test/README.md new file mode 100644 index 00000000..56a24223 --- /dev/null +++ b/prisma/migrations/20200524123205-test/README.md @@ -0,0 +1,40 @@ +# Migration `20200524123205-test` + +This migration has been generated by Pavel Strunkin at 5/24/2020, 12:32:05 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 20200524122552-baseline-history..20200524123205-test +--- datamodel.dml ++++ datamodel.dml +@@ -3,9 +3,9 @@ + } + datasource db { + provider = "postgresql" +- url = "***" ++ url = env("DATABASE_URL") + } + model Build { + id String @default(uuid()) @id +@@ -52,9 +52,9 @@ + viewport String? + baselineName String? + ignoreAreas String @default("[]") + // Baseline +- baseline Baseline ++ baseline Baseline? + } + model TestVariation { + id String @default(uuid()) @id +``` + + diff --git a/prisma/migrations/20200524123205-test/schema.prisma b/prisma/migrations/20200524123205-test/schema.prisma new file mode 100644 index 00000000..29d6533f --- /dev/null +++ b/prisma/migrations/20200524123205-test/schema.prisma @@ -0,0 +1,105 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = "***" +} + +model Build { + id String @default(uuid()) @id + number Int? + branchName String? + status String? + testRuns TestRun[] + projectId String + project Project @relation(fields: [projectId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + user User? @relation(fields: [userId], references: [id]) + userId String? +} + +model Project { + id String @default(uuid()) @id + name String + builds Build[] + testVariations TestVariation[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +model TestRun { + id String @default(uuid()) @id + imageName String + diffName String? + diffPercent Float? + diffTollerancePercent Float @default(1.0) + pixelMisMatchCount Int? + status TestStatus + buildId String + build Build @relation(fields: [buildId], references: [id]) + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + // Test variation data + name String @default("") + browser String? + device String? + os String? + viewport String? + baselineName String? + ignoreAreas String @default("[]") + // Baseline + baseline Baseline? +} + +model TestVariation { + id String @default(uuid()) @id + name String + browser String? + device String? + os String? + viewport String? + baselineName String? + ignoreAreas String @default("[]") + projectId String + project Project @relation(fields: [projectId], references: [id]) + testRuns TestRun[] + baselines Baseline[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +model Baseline { + id String @default(uuid()) @id + baselineName String + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + testVariationId String + testRunId String + testRun TestRun @relation(fields: [testRunId], references: [id]) +} + +model User { + id String @default(uuid()) @id + email String @unique + password String + firstName String? + lastName String? + apiKey String @unique + isActive Boolean @default(true) + builds Build[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +enum TestStatus { + failed + new + ok + unresolved +} \ No newline at end of file diff --git a/prisma/migrations/20200524123205-test/steps.json b/prisma/migrations/20200524123205-test/steps.json new file mode 100644 index 00000000..577bb8d8 --- /dev/null +++ b/prisma/migrations/20200524123205-test/steps.json @@ -0,0 +1,11 @@ +{ + "version": "0.3.14-fixed", + "steps": [ + { + "tag": "UpdateField", + "model": "TestRun", + "field": "baseline", + "arity": "Optional" + } + ] +} \ No newline at end of file diff --git a/prisma/migrations/migrate.lock b/prisma/migrations/migrate.lock index 19288137..1a6cd92f 100644 --- a/prisma/migrations/migrate.lock +++ b/prisma/migrations/migrate.lock @@ -4,4 +4,8 @@ # Read more about conflict resolution here: TODO 20200503001556-init -20200523130656-test-run-owns-variation-data \ No newline at end of file +20200523130656-test-run-owns-variation-data +20200524120957-baseline-history +20200524121750-baseline-history +20200524122552-baseline-history +20200524123205-test \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 1130026d..194d5e5d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -52,22 +52,36 @@ model TestRun { viewport String? baselineName String? ignoreAreas String @default("[]") + // Baseline + baseline Baseline? } model TestVariation { - id String @default(uuid()) @id + id String @default(uuid()) @id name String browser String? device String? os String? viewport String? baselineName String? - ignoreAreas String @default("[]") + ignoreAreas String @default("[]") projectId String - project Project @relation(fields: [projectId], references: [id]) + project Project @relation(fields: [projectId], references: [id]) testRuns TestRun[] - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) + baselines Baseline[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +model Baseline { + id String @default(uuid()) @id + baselineName String + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + testRunId String + testRun TestRun @relation(fields: [testRunId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) } model User { diff --git a/src/test-runs/test-runs.service.ts b/src/test-runs/test-runs.service.ts index 1bb980cc..3236d6fa 100644 --- a/src/test-runs/test-runs.service.ts +++ b/src/test-runs/test-runs.service.ts @@ -36,6 +36,23 @@ export class TestRunsService { const imageName = `${Date.now()}.baseline.png`; this.staticService.saveImage(imageName, PNG.sync.write(baseline)); + // add in baseline history + await this.prismaService.baseline.create({ + data: { + baselineName: imageName, + testRun: { + connect: { + id: testRun.id + } + }, + testVariation: { + connect: { + id: testRun.testVariationId + } + }, + } + }) + return this.prismaService.testRun.update({ where: { id }, data: { From ecb3be03e90824eb8ee5a3d787ca927f785ddbf9 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sun, 24 May 2020 15:29:57 +0200 Subject: [PATCH 11/16] test-variation. get details added --- .../test-variations.controller.ts | 12 +++++++++++- src/test-variations/test-variations.service.ts | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/test-variations/test-variations.controller.ts b/src/test-variations/test-variations.controller.ts index f97f5280..d23e9622 100644 --- a/src/test-variations/test-variations.controller.ts +++ b/src/test-variations/test-variations.controller.ts @@ -1,7 +1,7 @@ import { Controller, ParseUUIDPipe, Get, UseGuards, Param, Query, Put, Body } from '@nestjs/common'; import { ApiTags, ApiParam, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; import { TestVariationsService } from './test-variations.service'; -import { TestVariation } from '@prisma/client'; +import { TestVariation, Baseline } from '@prisma/client'; import { JwtAuthGuard } from '../auth/guards/auth.guard'; import { PrismaService } from '../prisma/prisma.service'; import { IgnoreAreaDto } from '../test/dto/ignore-area.dto'; @@ -23,6 +23,16 @@ export class TestVariationsController { }); } + @Get(':id') + @ApiQuery({ name: 'id', required: true }) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + getDetails( + @Param('id', new ParseUUIDPipe()) id, + ): Promise { + return this.testVariations.getDetails(id); + } + @Put('ignoreArea/:variationId') @ApiParam({ name: 'variationId', required: true }) @ApiBearerAuth() diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index 44060d9e..a163ce0f 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { CreateTestRequestDto } from '../test/dto/create-test-request.dto'; import { IgnoreAreaDto } from '../test/dto/ignore-area.dto'; import { PrismaService } from '../prisma/prisma.service'; -import { TestVariation } from '@prisma/client'; +import { TestVariation, Baseline } from '@prisma/client'; @Injectable() export class TestVariationsService { @@ -10,6 +10,22 @@ export class TestVariationsService { private prismaService: PrismaService, ) { } + async getDetails(id: string): Promise { + return this.prismaService.testVariation.findOne({ + where: { id }, + include: { + baselines: { + include: { + testRun: true, + }, + orderBy: { + createdAt: 'desc' + } + }, + }, + }); + } + async findOrCreate(createTestDto: CreateTestRequestDto): Promise { const data = { name: createTestDto.name, From 4e2828fc59198235ca02de67f003940d6991accb Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sun, 24 May 2020 16:22:11 +0200 Subject: [PATCH 12/16] test variation delete added --- .../README.md | 66 ----- .../schema.prisma | 91 ------- .../steps.json | 157 ------------ .../schema.prisma | 105 -------- .../migrations/20200524123205-test/README.md | 40 ---- .../migrations/20200524123205-test/steps.json | 11 - .../README.md | 60 +++-- .../schema.prisma | 8 +- .../steps.json | 226 +++++++++++++++--- prisma/migrations/migrate.lock | 6 +- prisma/schema.prisma | 4 +- src/projects/projects.service.ts | 3 +- .../test-variations.service.ts | 18 ++ 13 files changed, 260 insertions(+), 535 deletions(-) delete mode 100644 prisma/migrations/20200523130656-test-run-owns-variation-data/README.md delete mode 100644 prisma/migrations/20200523130656-test-run-owns-variation-data/schema.prisma delete mode 100644 prisma/migrations/20200523130656-test-run-owns-variation-data/steps.json delete mode 100644 prisma/migrations/20200524122552-baseline-history/schema.prisma delete mode 100644 prisma/migrations/20200524123205-test/README.md delete mode 100644 prisma/migrations/20200524123205-test/steps.json rename prisma/migrations/{20200524122552-baseline-history => 20200524162125-baseline-history}/README.md (55%) rename prisma/migrations/{20200524123205-test => 20200524162125-baseline-history}/schema.prisma (96%) rename prisma/migrations/{20200524122552-baseline-history => 20200524162125-baseline-history}/steps.json (60%) diff --git a/prisma/migrations/20200523130656-test-run-owns-variation-data/README.md b/prisma/migrations/20200523130656-test-run-owns-variation-data/README.md deleted file mode 100644 index 1dfc1d9c..00000000 --- a/prisma/migrations/20200523130656-test-run-owns-variation-data/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Migration `20200523130656-test-run-owns-variation-data` - -This migration has been generated by Pavel Strunkin at 5/23/2020, 1:06:56 PM. -You can check out the [state of the schema](./schema.prisma) after the migration. - -## Database Steps - -```sql -ALTER TABLE "public"."TestRun" ADD COLUMN "baselineName" text , -ADD COLUMN "browser" text , -ADD COLUMN "device" text , -ADD COLUMN "ignoreAreas" text NOT NULL DEFAULT '[]', -ADD COLUMN "name" text NOT NULL DEFAULT '', -ADD COLUMN "os" text , -ADD COLUMN "viewport" text ; -``` - -## Changes - -```diff -diff --git schema.prisma schema.prisma -migration 20200503001556-init..20200523130656-test-run-owns-variation-data ---- datamodel.dml -+++ datamodel.dml -@@ -3,9 +3,9 @@ - } - datasource db { - provider = "postgresql" -- url = "***" -+ url = env("DATABASE_URL") - } - model Build { - id String @default(uuid()) @id -@@ -14,12 +14,12 @@ - status String? - testRuns TestRun[] - projectId String - project Project @relation(fields: [projectId], references: [id]) -- // userId String -- // user User @relation(fields: [userId], references: [id]) - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -+ user User? @relation(fields: [userId], references: [id]) -+ userId String? - } - model Project { - id String @default(uuid()) @id -@@ -43,8 +43,16 @@ - testVariationId String - testVariation TestVariation @relation(fields: [testVariationId], references: [id]) - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -+ // Test variation data -+ name String @default("") -+ browser String? -+ device String? -+ os String? -+ viewport String? -+ baselineName String? -+ ignoreAreas String @default("[]") - } - model TestVariation { - id String @default(uuid()) @id -``` - - diff --git a/prisma/migrations/20200523130656-test-run-owns-variation-data/schema.prisma b/prisma/migrations/20200523130656-test-run-owns-variation-data/schema.prisma deleted file mode 100644 index 8dd1fa3d..00000000 --- a/prisma/migrations/20200523130656-test-run-owns-variation-data/schema.prisma +++ /dev/null @@ -1,91 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - url = "***" -} - -model Build { - id String @default(uuid()) @id - number Int? - branchName String? - status String? - testRuns TestRun[] - projectId String - project Project @relation(fields: [projectId], references: [id]) - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) - user User? @relation(fields: [userId], references: [id]) - userId String? -} - -model Project { - id String @default(uuid()) @id - name String - builds Build[] - testVariations TestVariation[] - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -} - -model TestRun { - id String @default(uuid()) @id - imageName String - diffName String? - diffPercent Float? - diffTollerancePercent Float @default(1.0) - pixelMisMatchCount Int? - status TestStatus - buildId String - build Build @relation(fields: [buildId], references: [id]) - testVariationId String - testVariation TestVariation @relation(fields: [testVariationId], references: [id]) - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) - // Test variation data - name String @default("") - browser String? - device String? - os String? - viewport String? - baselineName String? - ignoreAreas String @default("[]") -} - -model TestVariation { - id String @default(uuid()) @id - name String - browser String? - device String? - os String? - viewport String? - baselineName String? - ignoreAreas String @default("[]") - projectId String - project Project @relation(fields: [projectId], references: [id]) - testRuns TestRun[] - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -} - -model User { - id String @default(uuid()) @id - email String @unique - password String - firstName String? - lastName String? - apiKey String @unique - isActive Boolean @default(true) - builds Build[] - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -} - -enum TestStatus { - failed - new - ok - unresolved -} \ No newline at end of file diff --git a/prisma/migrations/20200523130656-test-run-owns-variation-data/steps.json b/prisma/migrations/20200523130656-test-run-owns-variation-data/steps.json deleted file mode 100644 index 1e17e222..00000000 --- a/prisma/migrations/20200523130656-test-run-owns-variation-data/steps.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "version": "0.3.14-fixed", - "steps": [ - { - "tag": "CreateField", - "model": "Build", - "field": "user", - "type": "User", - "arity": "Optional" - }, - { - "tag": "CreateDirective", - "location": { - "path": { - "tag": "Field", - "model": "Build", - "field": "user" - }, - "directive": "relation" - } - }, - { - "tag": "CreateArgument", - "location": { - "tag": "Directive", - "path": { - "tag": "Field", - "model": "Build", - "field": "user" - }, - "directive": "relation" - }, - "argument": "fields", - "value": "[userId]" - }, - { - "tag": "CreateArgument", - "location": { - "tag": "Directive", - "path": { - "tag": "Field", - "model": "Build", - "field": "user" - }, - "directive": "relation" - }, - "argument": "references", - "value": "[id]" - }, - { - "tag": "CreateField", - "model": "Build", - "field": "userId", - "type": "String", - "arity": "Optional" - }, - { - "tag": "CreateField", - "model": "TestRun", - "field": "name", - "type": "String", - "arity": "Required" - }, - { - "tag": "CreateDirective", - "location": { - "path": { - "tag": "Field", - "model": "TestRun", - "field": "name" - }, - "directive": "default" - } - }, - { - "tag": "CreateArgument", - "location": { - "tag": "Directive", - "path": { - "tag": "Field", - "model": "TestRun", - "field": "name" - }, - "directive": "default" - }, - "argument": "", - "value": "\"\"" - }, - { - "tag": "CreateField", - "model": "TestRun", - "field": "browser", - "type": "String", - "arity": "Optional" - }, - { - "tag": "CreateField", - "model": "TestRun", - "field": "device", - "type": "String", - "arity": "Optional" - }, - { - "tag": "CreateField", - "model": "TestRun", - "field": "os", - "type": "String", - "arity": "Optional" - }, - { - "tag": "CreateField", - "model": "TestRun", - "field": "viewport", - "type": "String", - "arity": "Optional" - }, - { - "tag": "CreateField", - "model": "TestRun", - "field": "baselineName", - "type": "String", - "arity": "Optional" - }, - { - "tag": "CreateField", - "model": "TestRun", - "field": "ignoreAreas", - "type": "String", - "arity": "Required" - }, - { - "tag": "CreateDirective", - "location": { - "path": { - "tag": "Field", - "model": "TestRun", - "field": "ignoreAreas" - }, - "directive": "default" - } - }, - { - "tag": "CreateArgument", - "location": { - "tag": "Directive", - "path": { - "tag": "Field", - "model": "TestRun", - "field": "ignoreAreas" - }, - "directive": "default" - }, - "argument": "", - "value": "\"[]\"" - } - ] -} \ No newline at end of file diff --git a/prisma/migrations/20200524122552-baseline-history/schema.prisma b/prisma/migrations/20200524122552-baseline-history/schema.prisma deleted file mode 100644 index 30648fad..00000000 --- a/prisma/migrations/20200524122552-baseline-history/schema.prisma +++ /dev/null @@ -1,105 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - url = "***" -} - -model Build { - id String @default(uuid()) @id - number Int? - branchName String? - status String? - testRuns TestRun[] - projectId String - project Project @relation(fields: [projectId], references: [id]) - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) - user User? @relation(fields: [userId], references: [id]) - userId String? -} - -model Project { - id String @default(uuid()) @id - name String - builds Build[] - testVariations TestVariation[] - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -} - -model TestRun { - id String @default(uuid()) @id - imageName String - diffName String? - diffPercent Float? - diffTollerancePercent Float @default(1.0) - pixelMisMatchCount Int? - status TestStatus - buildId String - build Build @relation(fields: [buildId], references: [id]) - testVariationId String - testVariation TestVariation @relation(fields: [testVariationId], references: [id]) - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) - // Test variation data - name String @default("") - browser String? - device String? - os String? - viewport String? - baselineName String? - ignoreAreas String @default("[]") - // Baseline - baseline Baseline -} - -model TestVariation { - id String @default(uuid()) @id - name String - browser String? - device String? - os String? - viewport String? - baselineName String? - ignoreAreas String @default("[]") - projectId String - project Project @relation(fields: [projectId], references: [id]) - testRuns TestRun[] - baselines Baseline[] - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -} - -model Baseline { - id String @default(uuid()) @id - baselineName String - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) - testVariation TestVariation @relation(fields: [testVariationId], references: [id]) - testVariationId String - testRunId String - testRun TestRun @relation(fields: [testRunId], references: [id]) -} - -model User { - id String @default(uuid()) @id - email String @unique - password String - firstName String? - lastName String? - apiKey String @unique - isActive Boolean @default(true) - builds Build[] - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) -} - -enum TestStatus { - failed - new - ok - unresolved -} \ No newline at end of file diff --git a/prisma/migrations/20200524123205-test/README.md b/prisma/migrations/20200524123205-test/README.md deleted file mode 100644 index 56a24223..00000000 --- a/prisma/migrations/20200524123205-test/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Migration `20200524123205-test` - -This migration has been generated by Pavel Strunkin at 5/24/2020, 12:32:05 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 20200524122552-baseline-history..20200524123205-test ---- datamodel.dml -+++ datamodel.dml -@@ -3,9 +3,9 @@ - } - datasource db { - provider = "postgresql" -- url = "***" -+ url = env("DATABASE_URL") - } - model Build { - id String @default(uuid()) @id -@@ -52,9 +52,9 @@ - viewport String? - baselineName String? - ignoreAreas String @default("[]") - // Baseline -- baseline Baseline -+ baseline Baseline? - } - model TestVariation { - id String @default(uuid()) @id -``` - - diff --git a/prisma/migrations/20200524123205-test/steps.json b/prisma/migrations/20200524123205-test/steps.json deleted file mode 100644 index 577bb8d8..00000000 --- a/prisma/migrations/20200524123205-test/steps.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "0.3.14-fixed", - "steps": [ - { - "tag": "UpdateField", - "model": "TestRun", - "field": "baseline", - "arity": "Optional" - } - ] -} \ No newline at end of file diff --git a/prisma/migrations/20200524122552-baseline-history/README.md b/prisma/migrations/20200524162125-baseline-history/README.md similarity index 55% rename from prisma/migrations/20200524122552-baseline-history/README.md rename to prisma/migrations/20200524162125-baseline-history/README.md index 7c9e546e..0f15cf76 100644 --- a/prisma/migrations/20200524122552-baseline-history/README.md +++ b/prisma/migrations/20200524162125-baseline-history/README.md @@ -1,27 +1,35 @@ -# Migration `20200524122552-baseline-history` +# Migration `20200524162125-baseline-history` -This migration has been generated by Pavel Strunkin at 5/24/2020, 12:25:52 PM. +This migration has been generated by Pavel Strunkin at 5/24/2020, 4:21:25 PM. You can check out the [state of the schema](./schema.prisma) after the migration. ## Database Steps ```sql CREATE TABLE "public"."Baseline" ( -"baselineName" text NOT NULL ,"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"id" text NOT NULL ,"testRunId" text NOT NULL ,"testVariationId" text NOT NULL ,"updatedAt" timestamp(3) NOT NULL , +"baselineName" text NOT NULL ,"createdAt" timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,"id" text NOT NULL ,"testRunId" text ,"testVariationId" text NOT NULL ,"updatedAt" timestamp(3) NOT NULL , PRIMARY KEY ("id")) +ALTER TABLE "public"."TestRun" ADD COLUMN "baselineName" text , +ADD COLUMN "browser" text , +ADD COLUMN "device" text , +ADD COLUMN "ignoreAreas" text NOT NULL DEFAULT '[]', +ADD COLUMN "name" text NOT NULL DEFAULT '', +ADD COLUMN "os" text , +ADD COLUMN "viewport" text ; + CREATE UNIQUE INDEX "Baseline_testRunId" ON "public"."Baseline"("testRunId") 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 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 20200523130656-test-run-owns-variation-data..20200524122552-baseline-history +migration 20200503001556-init..20200524162125-baseline-history --- datamodel.dml +++ datamodel.dml @@ -3,9 +3,9 @@ @@ -33,13 +41,35 @@ migration 20200523130656-test-run-owns-variation-data..20200524122552-baseline-h } model Build { id String @default(uuid()) @id -@@ -51,26 +51,40 @@ - os String? - viewport String? - baselineName String? - ignoreAreas String @default("[]") +@@ -14,12 +14,12 @@ + status String? + testRuns TestRun[] + projectId String + project Project @relation(fields: [projectId], references: [id]) +- // userId String +- // user User @relation(fields: [userId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) ++ user User? @relation(fields: [userId], references: [id]) ++ userId String? + } + model Project { + id String @default(uuid()) @id +@@ -43,26 +43,48 @@ + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) ++ // Test variation data ++ name String @default("") ++ browser String? ++ device String? ++ os String? ++ viewport String? ++ baselineName String? ++ ignoreAreas String @default("[]") + // Baseline -+ baseline Baseline ++ baseline Baseline? } model TestVariation { - id String @default(uuid()) @id @@ -65,12 +95,12 @@ migration 20200523130656-test-run-owns-variation-data..20200524122552-baseline-h +model Baseline { + id String @default(uuid()) @id + baselineName String ++ testVariationId String ++ testVariation TestVariation @relation(fields: [testVariationId], references: [id]) ++ testRunId String? ++ testRun TestRun? @relation(fields: [testRunId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) -+ testVariation TestVariation @relation(fields: [testVariationId], references: [id]) -+ testVariationId String -+ testRunId String -+ testRun TestRun @relation(fields: [testRunId], references: [id]) +} + model User { diff --git a/prisma/migrations/20200524123205-test/schema.prisma b/prisma/migrations/20200524162125-baseline-history/schema.prisma similarity index 96% rename from prisma/migrations/20200524123205-test/schema.prisma rename to prisma/migrations/20200524162125-baseline-history/schema.prisma index 29d6533f..9d436c64 100644 --- a/prisma/migrations/20200524123205-test/schema.prisma +++ b/prisma/migrations/20200524162125-baseline-history/schema.prisma @@ -76,12 +76,12 @@ model TestVariation { model Baseline { id String @default(uuid()) @id baselineName String + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + testRunId String? + testRun TestRun? @relation(fields: [testRunId], references: [id]) updatedAt DateTime @updatedAt createdAt DateTime @default(now()) - testVariation TestVariation @relation(fields: [testVariationId], references: [id]) - testVariationId String - testRunId String - testRun TestRun @relation(fields: [testRunId], references: [id]) } model User { diff --git a/prisma/migrations/20200524122552-baseline-history/steps.json b/prisma/migrations/20200524162125-baseline-history/steps.json similarity index 60% rename from prisma/migrations/20200524122552-baseline-history/steps.json rename to prisma/migrations/20200524162125-baseline-history/steps.json index 1b6b836b..0f5e22e1 100644 --- a/prisma/migrations/20200524122552-baseline-history/steps.json +++ b/prisma/migrations/20200524162125-baseline-history/steps.json @@ -55,6 +55,112 @@ "type": "String", "arity": "Required" }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "testVariationId", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "testVariation", + "type": "TestVariation", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testVariation" + }, + "directive": "relation" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testVariation" + }, + "directive": "relation" + }, + "argument": "fields", + "value": "[testVariationId]" + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testVariation" + }, + "directive": "relation" + }, + "argument": "references", + "value": "[id]" + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "testRunId", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "Baseline", + "field": "testRun", + "type": "TestRun", + "arity": "Optional" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testRun" + }, + "directive": "relation" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testRun" + }, + "directive": "relation" + }, + "argument": "fields", + "value": "[testRunId]" + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "Baseline", + "field": "testRun" + }, + "directive": "relation" + }, + "argument": "references", + "value": "[id]" + }, { "tag": "CreateField", "model": "Baseline", @@ -107,18 +213,18 @@ }, { "tag": "CreateField", - "model": "Baseline", - "field": "testVariation", - "type": "TestVariation", - "arity": "Required" + "model": "Build", + "field": "user", + "type": "User", + "arity": "Optional" }, { "tag": "CreateDirective", "location": { "path": { "tag": "Field", - "model": "Baseline", - "field": "testVariation" + "model": "Build", + "field": "user" }, "directive": "relation" } @@ -129,13 +235,13 @@ "tag": "Directive", "path": { "tag": "Field", - "model": "Baseline", - "field": "testVariation" + "model": "Build", + "field": "user" }, "directive": "relation" }, "argument": "fields", - "value": "[testVariationId]" + "value": "[userId]" }, { "tag": "CreateArgument", @@ -143,8 +249,8 @@ "tag": "Directive", "path": { "tag": "Field", - "model": "Baseline", - "field": "testVariation" + "model": "Build", + "field": "user" }, "directive": "relation" }, @@ -153,34 +259,27 @@ }, { "tag": "CreateField", - "model": "Baseline", - "field": "testVariationId", + "model": "Build", + "field": "userId", "type": "String", - "arity": "Required" + "arity": "Optional" }, { "tag": "CreateField", - "model": "Baseline", - "field": "testRunId", + "model": "TestRun", + "field": "name", "type": "String", "arity": "Required" }, - { - "tag": "CreateField", - "model": "Baseline", - "field": "testRun", - "type": "TestRun", - "arity": "Required" - }, { "tag": "CreateDirective", "location": { "path": { "tag": "Field", - "model": "Baseline", - "field": "testRun" + "model": "TestRun", + "field": "name" }, - "directive": "relation" + "directive": "default" } }, { @@ -189,13 +288,66 @@ "tag": "Directive", "path": { "tag": "Field", - "model": "Baseline", - "field": "testRun" + "model": "TestRun", + "field": "name" }, - "directive": "relation" + "directive": "default" }, - "argument": "fields", - "value": "[testRunId]" + "argument": "", + "value": "\"\"" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "browser", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "device", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "os", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "viewport", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "baselineName", + "type": "String", + "arity": "Optional" + }, + { + "tag": "CreateField", + "model": "TestRun", + "field": "ignoreAreas", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "TestRun", + "field": "ignoreAreas" + }, + "directive": "default" + } }, { "tag": "CreateArgument", @@ -203,20 +355,20 @@ "tag": "Directive", "path": { "tag": "Field", - "model": "Baseline", - "field": "testRun" + "model": "TestRun", + "field": "ignoreAreas" }, - "directive": "relation" + "directive": "default" }, - "argument": "references", - "value": "[id]" + "argument": "", + "value": "\"[]\"" }, { "tag": "CreateField", "model": "TestRun", "field": "baseline", "type": "Baseline", - "arity": "Required" + "arity": "Optional" }, { "tag": "CreateField", diff --git a/prisma/migrations/migrate.lock b/prisma/migrations/migrate.lock index 1a6cd92f..796c62b8 100644 --- a/prisma/migrations/migrate.lock +++ b/prisma/migrations/migrate.lock @@ -4,8 +4,4 @@ # Read more about conflict resolution here: TODO 20200503001556-init -20200523130656-test-run-owns-variation-data -20200524120957-baseline-history -20200524121750-baseline-history -20200524122552-baseline-history -20200524123205-test \ No newline at end of file +20200524162125-baseline-history \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 194d5e5d..bf247ca9 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -78,8 +78,8 @@ model Baseline { baselineName String testVariationId String testVariation TestVariation @relation(fields: [testVariationId], references: [id]) - testRunId String - testRun TestRun @relation(fields: [testRunId], references: [id]) + testRunId String? + testRun TestRun? @relation(fields: [testRunId], references: [id]) updatedAt DateTime @updatedAt createdAt DateTime @default(now()) } diff --git a/src/projects/projects.service.ts b/src/projects/projects.service.ts index 8310272f..575d746a 100644 --- a/src/projects/projects.service.ts +++ b/src/projects/projects.service.ts @@ -53,8 +53,7 @@ export class ProjectsService { try { await Promise.all(project.builds.map(build => this.buildsService.remove(build.id))); await Promise.all( - project.testVariations.map(testVariation => - this.testVariationsService.remove(testVariation.id), + project.testVariations.map(testVariation => this.testVariationsService.remove(testVariation.id) ), ); } catch (err) { diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index a163ce0f..6169982f 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -3,11 +3,13 @@ import { CreateTestRequestDto } from '../test/dto/create-test-request.dto'; import { IgnoreAreaDto } from '../test/dto/ignore-area.dto'; import { PrismaService } from '../prisma/prisma.service'; import { TestVariation, Baseline } from '@prisma/client'; +import { StaticService } from 'src/shared/static/static.service'; @Injectable() export class TestVariationsService { constructor( private prismaService: PrismaService, + private staticService: StaticService, ) { } async getDetails(id: string): Promise { @@ -64,6 +66,22 @@ export class TestVariationsService { } async remove(id: string): Promise { + const variation = await this.getDetails(id) + + // clear history + try { + await Promise.all( + variation.baselines.map(baseline => Promise.all([ + this.staticService.deleteImage(baseline.baselineName), + this.prismaService.baseline.delete({ + where: { id: baseline.id } + }) + ])) + ) + } catch (err) { + console.log(err) + } + return this.prismaService.testVariation.delete({ where: { id }, }); From 0f471ea3de65ac955e072776c442ae60504d9381 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sun, 24 May 2020 17:19:29 +0200 Subject: [PATCH 13/16] TestVariationsService. findOrCreate covered --- .../test-variations.service.spec.ts | 118 ++++++++++++++++-- .../test-variations.service.ts | 2 +- src/test/dto/create-test-request.dto.ts | 10 +- 3 files changed, 112 insertions(+), 18 deletions(-) diff --git a/src/test-variations/test-variations.service.spec.ts b/src/test-variations/test-variations.service.spec.ts index 931abfc0..86ed1e21 100644 --- a/src/test-variations/test-variations.service.spec.ts +++ b/src/test-variations/test-variations.service.spec.ts @@ -1,22 +1,116 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TestVariationsService } from './test-variations.service'; import { PrismaService } from '../prisma/prisma.service'; +import { CreateTestRequestDto } from '../test/dto/create-test-request.dto'; +import { StaticService } from '../shared/static/static.service'; + +const initModule = async ({ + findManyMock = jest.fn().mockReturnValue([]), + createMock = jest.fn(), +}) => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + TestVariationsService, + { provide: StaticService, useValue: {} }, + { + provide: PrismaService, useValue: { + testVariation: { + findMany: findManyMock, + create: createMock + } + } + }, + ], + }).compile(); + + return module.get(TestVariationsService); +} + +const dataRequiredFields: CreateTestRequestDto = { + buildId: 'buildId', + projectId: 'projectId', + name: 'Test name', + imageBase64: 'Image' +} + +const dataAllFields: CreateTestRequestDto = { + buildId: 'buildId', + projectId: 'projectId', + name: 'Test name', + imageBase64: 'Image', + os: 'OS', + browser: 'browser', + viewport: 'viewport', + device: 'device', +} describe('TestVariationsService', () => { let service: TestVariationsService; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - TestVariationsService, - { provide: PrismaService, useValue: {} }, - ], - }).compile(); + describe('findOrCreate', () => { + + it('can find by required fields', async () => { + const data = dataRequiredFields + const findManyMock = jest.fn() + service = await initModule({ findManyMock: findManyMock.mockResolvedValueOnce([data]) }) + + const result = await service.findOrCreate(data) + + expect(findManyMock).toHaveBeenCalledWith({ + where: { + name: data.name, + projectId: data.projectId, + os: null, + browser: null, + viewport: null, + device: null, + }, + }) + expect(result).toBe(data) + }) + + it('can find by all fields', async () => { + const data = dataAllFields + const findManyMock = jest.fn() + service = await initModule({ findManyMock: findManyMock.mockResolvedValueOnce([data]) }) + + const result = await service.findOrCreate(data) + + expect(findManyMock).toHaveBeenCalledWith({ + where: { + name: data.name, + projectId: data.projectId, + os: data.os, + browser: data.browser, + viewport: data.viewport, + device: data.device, + }, + }) + expect(result).toBe(data) + }) + + it('can create if not found', async () => { + const data = dataAllFields + const createMock = jest.fn() + service = await initModule({ createMock: createMock.mockResolvedValueOnce(data) }) - service = module.get(TestVariationsService); - }); + const result = await service.findOrCreate(data) - it('should be defined', () => { - expect(service).toBeDefined(); - }); + expect(createMock).toHaveBeenCalledWith({ + data: { + name: data.name, + os: data.os, + browser: data.browser, + viewport: data.viewport, + device: data.device, + project: { + connect: { + id: data.projectId, + } + } + }, + }) + expect(result).toBe(data) + }) + }) }); diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index 6169982f..0e698f02 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -3,7 +3,7 @@ import { CreateTestRequestDto } from '../test/dto/create-test-request.dto'; import { IgnoreAreaDto } from '../test/dto/ignore-area.dto'; import { PrismaService } from '../prisma/prisma.service'; import { TestVariation, Baseline } from '@prisma/client'; -import { StaticService } from 'src/shared/static/static.service'; +import { StaticService } from '../shared/static/static.service'; @Injectable() export class TestVariationsService { diff --git a/src/test/dto/create-test-request.dto.ts b/src/test/dto/create-test-request.dto.ts index 76f44f0f..b7ab1c18 100644 --- a/src/test/dto/create-test-request.dto.ts +++ b/src/test/dto/create-test-request.dto.ts @@ -13,22 +13,22 @@ export class CreateTestRequestDto { @ApiProperty() @IsOptional() @IsString() - os: string; + os?: string; @ApiProperty() @IsOptional() @IsString() - browser: string; + browser?: string; @ApiProperty() @IsOptional() @IsString() - viewport: string; + viewport?: string; @ApiProperty() @IsOptional() @IsString() - device: string; + device?: string; @ApiProperty() @IsUUID() @@ -41,5 +41,5 @@ export class CreateTestRequestDto { @ApiProperty() @IsOptional() @IsNumber() - diffTollerancePercent: number; + diffTollerancePercent?: number; } From e5acef58eac8393817a15bb675ec5a7f481c021c Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sun, 24 May 2020 17:24:36 +0200 Subject: [PATCH 14/16] TestVariationsService. updateIgnoreAreas covered --- .../test-variations.service.spec.ts | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/test-variations/test-variations.service.spec.ts b/src/test-variations/test-variations.service.spec.ts index 86ed1e21..c4e6e40c 100644 --- a/src/test-variations/test-variations.service.spec.ts +++ b/src/test-variations/test-variations.service.spec.ts @@ -3,10 +3,12 @@ import { TestVariationsService } from './test-variations.service'; import { PrismaService } from '../prisma/prisma.service'; import { CreateTestRequestDto } from '../test/dto/create-test-request.dto'; import { StaticService } from '../shared/static/static.service'; +import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; const initModule = async ({ findManyMock = jest.fn().mockReturnValue([]), createMock = jest.fn(), + updateMock = jest.fn() }) => { const module: TestingModule = await Test.createTestingModule({ providers: [ @@ -16,7 +18,8 @@ const initModule = async ({ provide: PrismaService, useValue: { testVariation: { findMany: findManyMock, - create: createMock + create: createMock, + update: updateMock, } } }, @@ -113,4 +116,31 @@ describe('TestVariationsService', () => { expect(result).toBe(data) }) }) + + describe('updateIgnoreAreas', () => { + it('can update', async () => { + const id = 'test id' + const ignoreAreas: IgnoreAreaDto[] = [ + { + x: 1, + y: 2.3, + width: 442.1, + height: 32.0 + } + ] + const updateMock = jest.fn() + service = await initModule({ updateMock }) + + await service.updateIgnoreAreas(id, ignoreAreas) + + expect(updateMock).toBeCalledWith({ + where: { + id + }, + data: { + ignoreAreas: JSON.stringify(ignoreAreas) + } + }) + }) + }) }); From 9a937ee3c1efe122a2010382fb96b16fa250f5e0 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sun, 24 May 2020 17:31:13 +0200 Subject: [PATCH 15/16] TestVariationsService. getDetails covered --- .../test-variations.service.spec.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/test-variations/test-variations.service.spec.ts b/src/test-variations/test-variations.service.spec.ts index c4e6e40c..eca1ec40 100644 --- a/src/test-variations/test-variations.service.spec.ts +++ b/src/test-variations/test-variations.service.spec.ts @@ -6,6 +6,7 @@ import { StaticService } from '../shared/static/static.service'; import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; const initModule = async ({ + findOneMock = jest.fn, findManyMock = jest.fn().mockReturnValue([]), createMock = jest.fn(), updateMock = jest.fn() @@ -17,6 +18,7 @@ const initModule = async ({ { provide: PrismaService, useValue: { testVariation: { + findOne: findOneMock, findMany: findManyMock, create: createMock, update: updateMock, @@ -50,6 +52,30 @@ const dataAllFields: CreateTestRequestDto = { describe('TestVariationsService', () => { let service: TestVariationsService; + describe('getDetails', () => { + it('can find one', async () => { + const id = 'test id' + const findOneMock = jest.fn() + service = await initModule({ findOneMock }) + + await service.getDetails(id) + + expect(findOneMock).toHaveBeenCalledWith({ + where: { id }, + include: { + baselines: { + include: { + testRun: true, + }, + orderBy: { + createdAt: 'desc' + } + }, + } + }) + }) + }) + describe('findOrCreate', () => { it('can find by required fields', async () => { From 6a3bd119621bb74ebe529127cc735ef5956393f7 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Sun, 24 May 2020 17:56:21 +0200 Subject: [PATCH 16/16] TestVariationsService. remove covered --- .../test-variations.service.spec.ts | 114 ++++++++++++++---- 1 file changed, 90 insertions(+), 24 deletions(-) diff --git a/src/test-variations/test-variations.service.spec.ts b/src/test-variations/test-variations.service.spec.ts index eca1ec40..01cc9982 100644 --- a/src/test-variations/test-variations.service.spec.ts +++ b/src/test-variations/test-variations.service.spec.ts @@ -4,24 +4,36 @@ import { PrismaService } from '../prisma/prisma.service'; import { CreateTestRequestDto } from '../test/dto/create-test-request.dto'; import { StaticService } from '../shared/static/static.service'; import { IgnoreAreaDto } from 'src/test/dto/ignore-area.dto'; +import { TestVariation, Baseline } from '@prisma/client'; const initModule = async ({ - findOneMock = jest.fn, - findManyMock = jest.fn().mockReturnValue([]), - createMock = jest.fn(), - updateMock = jest.fn() + imageDeleteMock = jest.fn(), + variationFindOneMock = jest.fn, + variationFindManyMock = jest.fn().mockReturnValue([]), + variationCreateMock = jest.fn(), + variationUpdateMock = jest.fn(), + variationDeleteMock = jest.fn(), + baselineDeleteMock = jest.fn() }) => { const module: TestingModule = await Test.createTestingModule({ providers: [ TestVariationsService, - { provide: StaticService, useValue: {} }, + { + provide: StaticService, useValue: { + deleteImage: imageDeleteMock + } + }, { provide: PrismaService, useValue: { testVariation: { - findOne: findOneMock, - findMany: findManyMock, - create: createMock, - update: updateMock, + findOne: variationFindOneMock, + findMany: variationFindManyMock, + create: variationCreateMock, + update: variationUpdateMock, + delete: variationDeleteMock, + }, + baseline: { + delete: baselineDeleteMock } } }, @@ -55,12 +67,12 @@ describe('TestVariationsService', () => { describe('getDetails', () => { it('can find one', async () => { const id = 'test id' - const findOneMock = jest.fn() - service = await initModule({ findOneMock }) + const variationFindOneMock = jest.fn() + service = await initModule({ variationFindOneMock }) await service.getDetails(id) - expect(findOneMock).toHaveBeenCalledWith({ + expect(variationFindOneMock).toHaveBeenCalledWith({ where: { id }, include: { baselines: { @@ -80,12 +92,12 @@ describe('TestVariationsService', () => { it('can find by required fields', async () => { const data = dataRequiredFields - const findManyMock = jest.fn() - service = await initModule({ findManyMock: findManyMock.mockResolvedValueOnce([data]) }) + const variationFindManyMock = jest.fn() + service = await initModule({ variationFindManyMock: variationFindManyMock.mockResolvedValueOnce([data]) }) const result = await service.findOrCreate(data) - expect(findManyMock).toHaveBeenCalledWith({ + expect(variationFindManyMock).toHaveBeenCalledWith({ where: { name: data.name, projectId: data.projectId, @@ -100,12 +112,12 @@ describe('TestVariationsService', () => { it('can find by all fields', async () => { const data = dataAllFields - const findManyMock = jest.fn() - service = await initModule({ findManyMock: findManyMock.mockResolvedValueOnce([data]) }) + const variationFindManyMock = jest.fn() + service = await initModule({ variationFindManyMock: variationFindManyMock.mockResolvedValueOnce([data]) }) const result = await service.findOrCreate(data) - expect(findManyMock).toHaveBeenCalledWith({ + expect(variationFindManyMock).toHaveBeenCalledWith({ where: { name: data.name, projectId: data.projectId, @@ -120,12 +132,12 @@ describe('TestVariationsService', () => { it('can create if not found', async () => { const data = dataAllFields - const createMock = jest.fn() - service = await initModule({ createMock: createMock.mockResolvedValueOnce(data) }) + const variationCreateMock = jest.fn() + service = await initModule({ variationCreateMock: variationCreateMock.mockResolvedValueOnce(data) }) const result = await service.findOrCreate(data) - expect(createMock).toHaveBeenCalledWith({ + expect(variationCreateMock).toHaveBeenCalledWith({ data: { name: data.name, os: data.os, @@ -154,12 +166,12 @@ describe('TestVariationsService', () => { height: 32.0 } ] - const updateMock = jest.fn() - service = await initModule({ updateMock }) + const variationUpdateMock = jest.fn() + service = await initModule({ variationUpdateMock }) await service.updateIgnoreAreas(id, ignoreAreas) - expect(updateMock).toBeCalledWith({ + expect(variationUpdateMock).toBeCalledWith({ where: { id }, @@ -169,4 +181,58 @@ describe('TestVariationsService', () => { }) }) }) + + describe('remove', () => { + it('can remove', async () => { + const id = 'test id' + const variation: TestVariation & { + baselines: Baseline[]; + } = { + id, + projectId: 'project Id', + name: 'Test name', + baselineName: 'baselineName', + os: 'OS', + browser: 'browser', + viewport: 'viewport', + device: 'device', + ignoreAreas: '[]', + createdAt: new Date(), + updatedAt: new Date(), + baselines: [ + { + id: 'baseline id 1', + baselineName: 'image name 1', + testVariationId: id, + testRunId: 'test run id 1', + createdAt: new Date(), + updatedAt: new Date(), + }, + ] + } + const variationFindOneMock = jest.fn() + const variationDeleteMock = jest.fn() + const imageDeleteMock = jest.fn() + const baselineDeleteMock = jest.fn() + service = await initModule( + { + variationFindOneMock: variationFindOneMock.mockResolvedValueOnce(variation), + variationDeleteMock, + imageDeleteMock, + baselineDeleteMock + }) + + await service.remove(id) + + expect(imageDeleteMock).toHaveBeenCalledWith( + variation.baselines[0].baselineName + ) + expect(baselineDeleteMock).toHaveBeenCalledWith({ + where: { id: variation.baselines[0].id } + }) + expect(variationDeleteMock).toHaveBeenCalledWith({ + where: { id: variation.id } + }) + }) + }) });