@syngrisi/syngrisi
Version:
Syngrisi - Visual Testing Tool
242 lines (203 loc) • 8.8 kB
text/typescript
import {
Check,
Test,
Suite,
Baseline,
CheckDocument,
} from '@models';
import { calculateAcceptedStatus, buildIdentObject } from '@utils';
import * as snapshotService from './snapshot.service';
import * as orm from '@lib/dbItems';
import log from '@lib/logger';
import { BaselineDocument } from '@models/Baseline.model';
import { Schema } from 'mongoose';
import { LogOpts, RequestUser } from '@root/src/types';
async function calculateTestStatus(testId: string): Promise<string> {
const checksInTest = await Check.find({ test: testId });
const statuses = checksInTest.map((x: CheckDocument) => x.status[0]);
let testCalculatedStatus = 'Failed';
if (statuses.every((x: string) => (x === 'new') || (x === 'passed'))) {
testCalculatedStatus = 'Passed';
}
if (statuses.every((x: string) => (x === 'new'))) {
testCalculatedStatus = 'New';
}
return testCalculatedStatus;
}
export interface baselineParamsType extends Document {
snapshootId?: string;
name: string;
app: string;
branch: string;
browserName: string;
browserVersion?: string;
browserFullVersion?: string;
viewport: string;
os: string;
markedAs?: 'bug' | 'accepted';
lastMarkedDate?: Date;
createdDate?: Date;
updatedDate?: Date;
markedById?: Schema.Types.ObjectId;
markedByUsername?: string;
ignoreRegions?: string;
boundRegions?: string;
matchType?: 'antialiasing' | 'nothing' | 'colors';
meta?: object;
actualSnapshotId: Schema.Types.ObjectId;
markedDate: Date;
}
const validateBaselineParam = (params: baselineParamsType): void => {
const mandatoryParams = ['markedAs', 'markedById', 'markedByUsername', 'markedDate'];
for (const param of mandatoryParams) {
if (!params[param as keyof baselineParamsType]) {
const errMsg = `invalid baseline parameters, '${param}' is empty, params: ${JSON.stringify(params)}`;
log.error(errMsg);
throw new Error(errMsg);
}
}
};
async function createNewBaseline(params: baselineParamsType): Promise<BaselineDocument> {
const logOpts = {
scope: 'createNewBaseline',
msgType: 'CREATE',
};
validateBaselineParam(params);
const identFields = buildIdentObject(params);
const lastBaseline = await Baseline.findOne(identFields).exec();
const sameBaseline = await Baseline.findOne({ ...identFields, snapshootId: params.actualSnapshotId }).exec();
const baselineParams = lastBaseline?.ignoreRegions
? { ...identFields, ignoreRegions: lastBaseline.ignoreRegions }
: identFields;
if (sameBaseline) {
log.debug(`the baseline with same ident and snapshot id: ${params.actualSnapshotId} already exist`, logOpts);
} else {
log.debug(`the baseline with same ident and snapshot id: ${params.actualSnapshotId} does not exist,
create new one, baselineParams: ${JSON.stringify(baselineParams)}`, logOpts);
}
log.silly({ sameBaseline });
const resultedBaseline: BaselineDocument = sameBaseline || await Baseline.create(baselineParams);
resultedBaseline.markedAs = params.markedAs;
resultedBaseline.markedById = params.markedById //? new Schema.Types.ObjectId(String(params.markedById)) : undefined;
resultedBaseline.markedByUsername = params.markedByUsername;
resultedBaseline.lastMarkedDate = params.markedDate;
resultedBaseline.createdDate = new Date();
resultedBaseline.snapshootId = params.actualSnapshotId //? new Schema.Types.ObjectId(String(params.actualSnapshotId)) : undefined;
return resultedBaseline.save();
}
const accept = async (id: string, baselineId: string, user: RequestUser): Promise<CheckDocument> => {
const logOpts = {
msgType: 'ACCEPT',
itemType: 'check',
ref: id,
user: user?.username,
scope: 'accept',
};
log.debug(`accept check: ${id}`, logOpts);
const check = await Check.findById(id).exec();
if (!check) throw new Error(`cannot find check with id: ${id}`);
const test = await Test.findById(check.test).exec();
if (!test) throw new Error(`cannot find test with id: ${check.test}`);
check.markedById = user._id;
check.markedByUsername = user.username;
check.markedDate = new Date();
check.markedAs = 'accepted';
check.status = (check.status[0] === 'new') ? ['new'] : ['passed'];
check.updatedDate = new Date();
// TODO: remove this line
// check.baselineId = baselineId as unknown as Schema.Types.ObjectId;
log.debug(`update check with options: '${JSON.stringify(check.toObject())}'`, logOpts);
await createNewBaseline(check.toObject());
await check.save();
const testCalculatedStatus = await calculateTestStatus(String(check.test));
const testCalculatedAcceptedStatus = await calculateAcceptedStatus(check.test);
test.status = testCalculatedStatus;
test.markedAs = testCalculatedAcceptedStatus;
test.updatedDate = new Date();
await Suite.findByIdAndUpdate(check.suite, { updatedDate: Date.now() });
log.debug(`update test with status: '${testCalculatedStatus}', marked: '${testCalculatedAcceptedStatus}'`, logOpts, {
msgType: 'UPDATE',
itemType: 'test',
ref: test._id,
});
await test.save();
await check.save();
log.debug(`check with id: '${id}' was updated`, logOpts);
return check;
};
async function removeCheck(id: string, user: RequestUser): Promise<CheckDocument> {
const logMeta = {
scope: 'removeCheck',
itemType: 'check',
ref: id,
msgType: 'REMOVE',
user: user?.username,
};
try {
const check = (await Check.findByIdAndDelete(id).exec()) as unknown as CheckDocument;
if (!check) throw new Error(`cannot find check with id: ${id}`);
log.debug(`check with id: '${id}' was removed, update test: ${check.test}`, logMeta);
const test = await Test.findById(check.test).exec();
if (!test) throw new Error(`cannot find test with id: ${check.test}`);
const testCalculatedStatus = await calculateTestStatus(String(check.test));
const testCalculatedAcceptedStatus = await calculateAcceptedStatus(check.test);
test.status = testCalculatedStatus;
test.markedAs = testCalculatedAcceptedStatus;
test.updatedDate = new Date();
await orm.updateItemDate('VRSSuite', check.suite);
await test.save();
if (check.baselineId && String(check.baselineId) !== 'undefined') {
log.debug(`try to remove the snapshot, baseline: ${check.baselineId}`, logMeta);
await snapshotService.remove(check.baselineId.toString());
}
if (check.actualSnapshotId && String(check.baselineId) !== 'undefined') {
log.debug(`try to remove the snapshot, actual: ${check.actualSnapshotId}`, logMeta);
await snapshotService.remove(check.actualSnapshotId.toString());
}
if (check.diffId && String(check.baselineId) !== 'undefined') {
log.debug(`try to remove snapshot, diff: ${check.diffId}`, logMeta);
await snapshotService.remove(check.diffId.toString());
}
return check;
} catch (e: unknown) {
const errMsg = `cannot remove a check with id: '${id}', error: '${e instanceof Error ? e.stack : String(e)}'`;
log.error(errMsg, logMeta);
throw new Error(errMsg);
}
}
const remove = async (id: string, user: RequestUser): Promise<CheckDocument> => {
const logOpts = {
scope: 'removeCheck',
itemType: 'check',
ref: id,
user: user?.username,
msgType: 'REMOVE',
};
log.info(`remove check with, id: '${id}', user: '${user.username}'`, logOpts);
return removeCheck(id, user);
};
const update = async (id: string, opts: Partial<CheckDocument>, user: string): Promise<CheckDocument> => {
const logMeta: LogOpts = {
msgType: 'UPDATE',
itemType: 'check',
ref: id,
user,
scope: 'updateCheck',
};
log.debug(`update check with id '${id}' with params '${JSON.stringify(opts, null, 2)}'`, logMeta);
const check = await Check.findOneAndUpdate({ _id: id }, opts, { new: true }).exec();
if (!check) throw new Error(`cannot find check with id: ${id}`);
const test = await Test.findOne({ _id: check.test }).exec();
if (!test) throw new Error(`cannot find test with id: ${check.test}`);
test.status = await calculateTestStatus(String(check.test));
await orm.updateItemDate('VRSCheck', check);
await orm.updateItemDate('VRSTest', test);
await test.save();
await check.save();
return check;
};
export {
accept,
remove,
update,
};