replace old test framework with cypress, migrate the tests

This commit is contained in:
Val Erastov (xibyte) 2020-03-24 00:07:03 -07:00
parent 13eb317c89
commit 6d7d2648e3
115 changed files with 1167 additions and 822 deletions

9
cypress.json Normal file
View file

@ -0,0 +1,9 @@
{
"watchForFileChanges": false,
"fixturesFolder": "test/cypress/fixtures",
"integrationFolder": "test/cypress/integration",
"pluginsFile": "test/cypress/plugins/index.js",
"screenshotsFolder": "test/cypress/screenshots",
"videosFolder": "test/cypress/videos",
"supportFile": "test/cypress/support/index.js"
}

View file

@ -1,3 +1,4 @@
// @deprecated - use streams
export default class Bus { export default class Bus {
constructor() { constructor() {

1005
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,10 +4,10 @@
"description": "JS.Sketcher is a parametric 2D and 3D CAD modeler written in pure javascript", "description": "JS.Sketcher is a parametric 2D and 3D CAD modeler written in pure javascript",
"scripts": { "scripts": {
"start": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.config.js --content-base web/ --port 3000 --host 0.0.0.0", "start": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.config.js --content-base web/ --port 3000 --host 0.0.0.0",
"start-test": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.config.dev.js --content-base web/ --port 3000 --host 0.0.0.0",
"pack": "node ./node_modules/webpack/bin/webpack.js --config webpack.config.js --progress --profile --colors", "pack": "node ./node_modules/webpack/bin/webpack.js --config webpack.config.js --progress --profile --colors",
"build": "grunt", "build": "grunt",
"lint": "eslint web/app" "lint": "eslint web/app",
"cypress": "npx cypress open"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -32,11 +32,13 @@
"@babel/preset-flow": "^7.0.0", "@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
"@babel/preset-stage-2": "^7.0.0", "@babel/preset-stage-2": "^7.0.0",
"@cypress/webpack-preprocessor": "^4.1.3",
"babel-eslint": "^10.0.2", "babel-eslint": "^10.0.2",
"babel-loader": "^8.0.4", "babel-loader": "^8.0.4",
"babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-plugin-transform-decorators-legacy": "^1.3.5",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"css-loader": "0.28.7", "css-loader": "0.28.7",
"cypress": "^4.2.0",
"eslint": "4.13.1", "eslint": "4.13.1",
"eslint-plugin-babel": "4.1.2", "eslint-plugin-babel": "4.1.2",
"eslint-plugin-import": "2.8.0", "eslint-plugin-import": "2.8.0",

View file

@ -0,0 +1,58 @@
import {camelCaseSplitToStr} from "gems/camelCaseSplit";
import {TestEnv} from "./test";
import {ModesConfig} from "./modes";
export function defineCypressTest(groupName, module) {
if (!module.TEST_MODE) {
throw 'modules should have a mode defined';
}
let hasOnly = false;
const tests = Object.keys(module).filter(key => key.startsWith('test')).map(key => {
const func = module[key];
if (func.only) {
hasOnly = true;
}
return {
name: camelCaseSplitToStr(key.substring("test".length)),
funcName: key,
func,
...ModesConfig[module.TEST_MODE]
};
});
(hasOnly ? describe.only : describe)(groupName, () => {
for (let test of tests) {
(test.func.only ? it.only : it)(test.name, () => {
cy.visit(test.startPage);
cy.window().then(win => {
cy.log("Core Test: " + test.funcName);
return new Promise((resolve, reject) => {
const subject = test.testSubject(win);
const testEnv = new TestEnv(() => {
cy.log("took: " + durationFormat(testEnv.took));
resolve();
});
test.func(testEnv, subject);
});
})
});
}
})
}
function durationFormat(millis){
function fixed(v) {
return v.toFixed(2);
}
if (millis < 1000) {
return fixed(millis) + "ms";
} else {
return fixed(millis / 1000) + "s";
}
}

13
test/coreTests/modes.js Normal file
View file

@ -0,0 +1,13 @@
import modellerUISubject from "./subjects/modellerUISubject";
import {createSketcherSubject} from "./subjects/sketcherUISubject";
export const ModesConfig = {
modellerUI: {
testSubject: win => modellerUISubject(win.__CAD_APP),
startPage: 'http://localhost:3000/index.html#TestProject'
},
sketcherUI: {
testSubject: win => createSketcherSubject(win.__CAD_APP),
startPage: 'http://localhost:3000/index.html#TestProject'
},
};

View file

@ -3,8 +3,8 @@ import {
ALL_EXCLUDING_SOLID_KINDS, ALL_EXCLUDING_SOLID_KINDS,
PICK_KIND, PICK_KIND,
traversePickResults traversePickResults
} from '../../../../app/cad/scene/controls/pickControlPlugin'; } from '../../../web/app/cad/scene/controls/pickControlPlugin';
import {DATUM} from '../../../../app/cad/scene/entites'; import {DATUM} from '../../../web/app/cad/scene/entites';
export default ctx => { export default ctx => {

View file

@ -1,7 +1,7 @@
import * as sketcher_utils from '../../../utils/sketcher-utils' import * as sketcher_utils from '../utils/sketcherUtils'
import {decapitalize} from '../../../../../modules/gems/capitalize'; import {decapitalize} from '../../../modules/gems/capitalize';
import genSerpinski, {genSerpinskiImpl} from '../../../../app/utils/genSerpinski'; import genSerpinski, {genSerpinskiImpl} from '../../../web/app/utils/genSerpinski';
import {distance, distanceAB} from '../../../../app/math/math'; import {distance, distanceAB} from '../../../web/app/math/math';
export function createSubjectFromInPlaceSketcher(ctx) { export function createSubjectFromInPlaceSketcher(ctx) {
let actions = {}; let actions = {};

View file

@ -1,10 +1,4 @@
export function createFailError(msg) {
let error = new Error(msg);
error.assertionFail = true;
return error;
}
export class TestEnv { export class TestEnv {
constructor(callback) { constructor(callback) {
@ -28,7 +22,7 @@ export class TestEnv {
this.failed = true; this.failed = true;
this.error = msg + (optionalMsg === undefined ? '' : ' ' + optionalMsg); this.error = msg + (optionalMsg === undefined ? '' : ' ' + optionalMsg);
this.done(); this.done();
throw createFailError(this.error); throw assert.fail(this.error);
} }
terminateOnError(error) { terminateOnError(error) {
@ -144,60 +138,3 @@ function checkSimilarity(data1, data2) {
return info1 === info2; return info1 === info2;
} }
export function load(url, callback, apiGet) {
const sandbox = $('#sandbox');
sandbox.empty();
const frame = $('<iframe>');
sandbox.append(frame);
$(function() { // fire event when iframe is ready
frame.on('load', function() {
const win = frame.get(0).contentWindow;
callback(win, apiGet(win))
});
});
frame.attr('src', window.location.origin + url)
}
const TEST_PROJECT = '$$$__test__$$$';
const STORAGE_PREFIX_SKETCH = "TCAD.projects.";
const SKETCHER_API = win => win.__CAD_APP;
const MODELLER_API = win => ({TPI: win.__CAD_APP.services.tpi});
export function emptySketch(callback) {
localStorage.removeItem(STORAGE_PREFIX_SKETCH + TEST_PROJECT);
sketch(callback);
}
export function sketch(callback) {
load('/sketcher.html#' + TEST_PROJECT, callback, SKETCHER_API);
}
let ModellerWin = null;
export function modeller(callback) {
if (ModellerWin != null) {
ModellerWin.__CAD_APP.services.project.empty();
callback(ModellerWin, MODELLER_API(ModellerWin));
return;
}
load('/index.html#' + TEST_PROJECT, (win, api) => {
ModellerWin = win;
let ctx = win.__CAD_APP;
ctx.streams.lifecycle.projectLoaded.attach(loaded => {
if (loaded) {
callback(win, api);
}
})
}, MODELLER_API);
}
export function emptyModeller(callback) {
for(let i = localStorage.length - 1; i >= 0 ; i--) {
const key = localStorage.key(i);
if (key.startsWith(STORAGE_PREFIX_SKETCH + TEST_PROJECT)) {
localStorage.removeItem(key);
}
}
modeller(callback);
}

View file

@ -1,4 +1,4 @@
import {DATUM} from '../../app/cad/scene/entites'; import {DATUM} from '../../../web/app/cad/scene/entites';
import {assertEquals, assertTrue} from '../utils/asserts'; import {assertEquals, assertTrue} from '../utils/asserts';
export const TEST_MODE = 'modellerUI'; export const TEST_MODE = 'modellerUI';

View file

@ -1,5 +1,5 @@
import {assertEquals, assertTrue} from '../utils/asserts'; import {assertEquals, assertTrue} from '../utils/asserts';
import {DATUM} from '../../app/cad/scene/entites'; import {DATUM} from '../../../web/app/cad/scene/entites';
export const TEST_MODE = 'modellerUI'; export const TEST_MODE = 'modellerUI';

View file

@ -1,4 +1,4 @@
import {defineTests} from './craftTestUtils'; import {defineTests} from '../craftTestUtils';
export default defineTests([ export default defineTests([

View file

@ -1,4 +1,4 @@
import {defineTests} from './craftTestUtils'; import {defineTests} from '../craftTestUtils';
export default defineTests([ export default defineTests([

View file

@ -1,4 +1,4 @@
import * as test from '../test' import * as test from '../../test'
export default { export default {

View file

@ -1,6 +1,6 @@
import * as test from '../test' import * as test from '../../test'
import {deepMerge} from '../utils/deep-merge' import {deepMerge} from '../coreTests/utils/deep-merge'
import {Matrix3} from '../../app/math/l3space' import {Matrix3} from '../../../../web/app/math/l3space'
const OPERANDS_MODE = false; const OPERANDS_MODE = false;

View file

@ -1,4 +1,4 @@
import * as test from '../test' import * as test from '../../test'
export default { export default {

View file

@ -1,4 +1,4 @@
import * as test from '../test' import * as test from '../../test'
export default { export default {

View file

@ -1,4 +1,4 @@
import * as test from '../test' import * as test from '../../test'
const TESTS = {}; const TESTS = {};
let counter = 0; let counter = 0;

View file

@ -1,5 +1,5 @@
import * as test from '../test' import * as test from '../../test'
import * as sketcher_utils from '../utils/sketcher-utils' import * as sketcher_utils from '../../utils/sketcherUtils'
export default { export default {
testCoincident: function (env) { testCoincident: function (env) {

View file

@ -1,6 +1,6 @@
import * as test from '../test' import * as test from '../test'
import * as sketcher_utils from '../utils/sketcher-utils' import * as sketcher_utils from '../utils/sketcherUtils'
import {TestMouseEvent} from '../utils/mouse-event' import {TestMouseEvent} from '../utils/mouseEvent'
import Vector from 'math/vector'; import Vector from 'math/vector';
export default { export default {

View file

@ -1,7 +1,7 @@
import * as test from '../test' import * as test from '../test'
import * as sketcher_utils from '../utils/sketcher-utils' import * as sketcher_utils from '../utils/sketcherUtils'
import * as keyboard from '../utils/keyboard' import * as keyboard from '../utils/keyboard'
import {TestMouseEvent} from '../utils/mouse-event' import {TestMouseEvent} from '../utils/mouseEvent'
import Vector from 'math/vector'; import Vector from 'math/vector';
export default { export default {

View file

@ -1,8 +1,8 @@
import {createFailError} from '../test'; import {createFailError} from '../test';
import sketchObjectGlobalId from '../../app/cad/sketch/sketchObjectGlobalId'; import sketchObjectGlobalId from '../../../web/app/cad/sketch/sketchObjectGlobalId';
export function fail(msg, optionalMsg) { export function fail(msg, optionalMsg) {
throw createFailError(msg + (optionalMsg === undefined ? '' : ' ' + optionalMsg)); assert.fail(msg + (optionalMsg === undefined ? '' : ' ' + optionalMsg));
} }
export function assertTrue(stmt, msg) { export function assertTrue(stmt, msg) {
@ -15,7 +15,7 @@ export function assertTrue(stmt, msg) {
} }
export function assertEmpty(array, msg) { export function assertEmpty(array, msg) {
if (typeof stmt === 'string') { if (typeof msg === 'string') {
throw 'wrong assertion usage, mixed up arguments'; throw 'wrong assertion usage, mixed up arguments';
} }
if (array.length !== 0) { if (array.length !== 0) {
@ -48,7 +48,7 @@ export function assertPointXY2DEquals(expectedX, expectedY, actual, msg) {
} }
export function assertPoint2DEquals(expected, actial, msg) { export function assertPoint2DEquals(expected, actial, msg) {
this.assertPointXY2DEquals(expected.x, expected.y, actial, msg); assertPointXY2DEquals(expected.x, expected.y, actial, msg);
} }
export function assertFaceIsPlane(face) { export function assertFaceIsPlane(face) {

View file

@ -1,4 +1,4 @@
import {TestMouseEvent} from './mouse-event' import {TestMouseEvent} from './mouseEvent'
import Vector from 'math/vector'; import Vector from 'math/vector';
export function toModel(app, x, y) { export function toModel(app, x, y) {

2
test/cypress/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/screenshots/
/videos/

View file

@ -0,0 +1,30 @@
/// <reference types="cypress" />
import * as PlaneTests from '../../../coreTests/testCases/craftPlane';
import * as ExtrudeBasicShapesTests from '../../../coreTests/testCases/craftExtrudeBasicShapes';
import * as ExtrudeOptionsTests from '../../../coreTests/testCases/craftExtrudeOptions';
import * as ExtrudeTests from '../../../coreTests/testCases/craftExtrude';
import * as CutTests from '../../../coreTests/testCases/craftCut';
import * as RevolveTests from '../../../coreTests/testCases/craftRevolve';
import * as FilletTests from '../../../coreTests/testCases/craftFillet';
import * as LoftTests from '../../../coreTests/testCases/craftLoft';
import * as DatumTests from '../../../coreTests/testCases/craftDatum';
import * as BooleanTests from '../../../coreTests/testCases/craftBoolean';
import {defineCypressTest} from "../../../coreTests/defineCypress";
describe("Craft Operations", () => {
defineCypressTest("Plane Operation", PlaneTests);
defineCypressTest("Extrude - all sketcher objects", ExtrudeBasicShapesTests);
defineCypressTest("Extrude Options", ExtrudeOptionsTests);
defineCypressTest("Extrude Operation", ExtrudeTests);
defineCypressTest("Cut Operation", CutTests);
defineCypressTest("Revolve Operation", RevolveTests);
defineCypressTest("Fillet Operation", FilletTests);
defineCypressTest("Loft Operation", LoftTests);
defineCypressTest("Datum Operation", DatumTests);
defineCypressTest("General Boolean Operation", BooleanTests);
});

View file

@ -0,0 +1,29 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
const webpack = require('@cypress/webpack-preprocessor')
module.exports = (on) => {
const options = {
// send in the options from your webpack.config.js, so it works the same
// as your app's code
webpackOptions: require('../../../webpack.config'),
watchOptions: {},
};
on('file:preprocessor', webpack(options))
};

View file

@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })

Some files were not shown because too many files have changed in this diff Show more