Skip to content

Commit

Permalink
Implement test and fixture modifiers (DevExpress#1107)
Browse files Browse the repository at this point in the history
* Implement test.only and fixture.only

* Move logic for test.only filtering to bootstrapper.

* Implement test.skip and fixture.skip

* Add tests for test.skip

* Fix test error. Implement test.page. Refactor.

* Fix raw API.

* Add test for test.page

* Implement test.before and test.after hooks

* Fix tests

* Tests for test.before and test.after

* Implement test.httpAuth. Add tests for test.before and test.after args

* Test test.httpAuth. Fix compiler test file heuristics

* Implement t.ctx

* Make t.ctx assignable

* Fix compiler filter RegExps
  • Loading branch information
inikulin authored Jan 10, 2017
1 parent 7a037c7 commit 540c5a6
Show file tree
Hide file tree
Showing 50 changed files with 760 additions and 238 deletions.
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@
"testcafe-browser-tools": "1.1.7",
"testcafe-hammerhead": "10.2.1",
"testcafe-legacy-api": "^2.0.0",
"testcafe-reporter-json": "^2.0.0",
"testcafe-reporter-list": "^2.0.0",
"testcafe-reporter-minimal": "^2.0.0",
"testcafe-reporter-spec": "^2.0.0",
"testcafe-reporter-xunit": "^2.0.0",
"testcafe-reporter-json": "^2.1.0",
"testcafe-reporter-list": "^2.1.0",
"testcafe-reporter-minimal": "^2.1.0",
"testcafe-reporter-spec": "^2.1.1",
"testcafe-reporter-xunit": "^2.1.0",
"time-limit-promise": "^1.0.2",
"tree-kill": "^1.1.0",
"useragent": "^2.1.7"
Expand Down
69 changes: 24 additions & 45 deletions src/api/globals/fixture.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,57 @@
import { APIError } from '../../errors/runtime';
import MESSAGE from '../../errors/runtime/message';
import handleTagArgs from '../../utils/handle-tag-args';
import wrapTestFunction from './wrap-test-function';
import { assertObject, assertString } from '../../errors/runtime/type-assertions';
import TestingUnit from './testing-unit';

const PROTOCOL_RE = /^https?:\/\//;
const IMPLICIT_PROTOCOL_RE = /^\/\//;
export default class Fixture extends TestingUnit {
constructor (globals) {
super(globals);

export default class Fixture {
constructor (name, filename) {
var nameType = typeof name;
this.path = globals.filename;

if (nameType !== 'string')
throw new APIError('fixture', MESSAGE.fixtureNameIsNotAString, nameType);
this.pageUrl = 'about:blank';

this.name = name;
this.path = filename;
this.pageUrl = 'about:blank';
this.beforeEachFn = null;
this.afterEachFn = null;
this.authCredentials = null;
}

page (url, ...rest) {
this.pageUrl = handleTagArgs(url, rest);

var urlType = typeof this.pageUrl;

if (urlType !== 'string')
throw new APIError('page', MESSAGE.fixturePageIsNotAString, urlType);

if (!PROTOCOL_RE.test(this.pageUrl)) {
var protocol = IMPLICIT_PROTOCOL_RE.test(this.pageUrl) ? 'http:' : 'http://';

this.pageUrl = protocol + this.pageUrl;
}

return this;
return this.apiOrigin;
}

httpAuth (credentials) {
assertObject('httpAuth', 'credentials', credentials);
assertString('httpAuth', 'credentials.username', credentials.username);
assertString('httpAuth', 'credentials.password', credentials.password);
_add (name, ...rest) {
name = handleTagArgs(name, rest);

var nameType = typeof name;

if (credentials.domain)
assertString('httpAuth', 'credentials.domain', credentials.domain);
if (credentials.workstation)
assertString('httpAuth', 'credentials.workstation', credentials.workstation);
if (nameType !== 'string')
throw new APIError('apiOrigin', MESSAGE.fixtureNameIsNotAString, nameType);

this.authCredentials = credentials;
this.name = name;
this.globals.currentFixture = this;

return this;
return this.apiOrigin;
}

beforeEach (fn) {
_beforeEach$ (fn) {
var fnType = typeof fn;

if (fnType !== 'function')
throw new APIError('beforeEach', MESSAGE.beforeEachIsNotAFunction, fnType);

this.beforeEachFn = wrapTestFunction(fn);
this.beforeEachFn = TestingUnit._wrapTestFunction(fn);

return this;
return this.apiOrigin;
}

afterEach (fn) {
_afterEach$ (fn) {
var fnType = typeof fn;

if (fnType !== 'function')
throw new APIError('afterEach', MESSAGE.afterEachIsNotAFunction, fnType);

this.afterEachFn = wrapTestFunction(fn);
this.afterEachFn = TestingUnit._wrapTestFunction(fn);

return this;
return this.apiOrigin;
}
}

TestingUnit._makeAPIListForChildClass(Fixture);
33 changes: 10 additions & 23 deletions src/api/globals/index.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,27 @@
import Fixture from './fixture';
import Test from './test';
import handleTagArgs from '../../utils/handle-tag-args';

export default class Globals {
constructor (filename) {
this.filename = filename;
this.currentFixture = null;
this.collectedTests = [];

var globals = this;

// NOTE: use named functions so that they appear in the stacks
this.functions = {
fixture (name, ...rest) {
name = handleTagArgs(name, rest);

globals.currentFixture = new Fixture(name, globals.filename);

return globals.currentFixture;
},

test (name, fn) {
var test = new Test(name, fn, globals.currentFixture);

globals.collectedTests.push(test);
}
};
}

setup () {
Object.keys(this.functions).forEach(name => {
global[name] = this.functions[name];
Object.defineProperty(global, 'fixture', {
get: () => new Fixture(this),
configurable: true
});

Object.defineProperty(global, 'test', {
get: () => new Test(this),
configurable: true
});
}

remove () {
Object.keys(this.functions).forEach(name => delete global[name]);
delete global.fixture;
delete global.test;
}
}
51 changes: 43 additions & 8 deletions src/api/globals/test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,56 @@
import { APIError } from '../../errors/runtime';
import MESSAGE from '../../errors/runtime/message';
import wrapTestFunction from './wrap-test-function';
import TestingUnit from './testing-unit';
import { assertFunction } from '../../errors/runtime/type-assertions';

export default class Test {
constructor (name, fn, fixture) {
export default class Test extends TestingUnit {
constructor (globals) {
super(globals);

this.fixture = globals.currentFixture;

this.fn = null;
this.beforeFn = null;
this.afterFn = null;

return this.apiOrigin;
}

_add (name, fn) {
var nameType = typeof name;

if (nameType !== 'string')
throw new APIError('test', MESSAGE.testNameIsNotAString, nameType);
throw new APIError('apiOrigin', MESSAGE.testNameIsNotAString, nameType);

var fnType = typeof fn;

if (fnType !== 'function')
throw new APIError('test', MESSAGE.testBodyIsNotAFunction, fnType);
throw new APIError('apiOrigin', MESSAGE.testBodyIsNotAFunction, fnType);

this.name = name;
this.fn = TestingUnit._wrapTestFunction(fn);

if (this.globals.collectedTests.indexOf(this) < 0)
this.globals.collectedTests.push(this);

return this.apiOrigin;
}

_before$ (fn) {
assertFunction('before', 'test.before hook', fn);

this.beforeFn = TestingUnit._wrapTestFunction(fn);

return this.apiOrigin;
}

_after$ (fn) {
assertFunction('after', 'test.after hook', fn);

this.name = name;
this.fixture = fixture;
this.fn = wrapTestFunction(fn);
this.afterFn = TestingUnit._wrapTestFunction(fn);

return this.apiOrigin;
}
}

TestingUnit._makeAPIListForChildClass(Test);
110 changes: 110 additions & 0 deletions src/api/globals/testing-unit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { APIError } from '../../errors/runtime';
import MESSAGE from '../../errors/runtime/message';
import handleTagArgs from '../../utils/handle-tag-args';
import { delegateAPI, getDelegatedAPIList } from '../../utils/delegated-api';
import { assertObject, assertString } from '../../errors/runtime/type-assertions';
import TestController from '../test-controller';
import clientFnTestRunTracker from '../../client-functions/test-run-tracker';
import processTestFnError from '../../errors/process-test-fn-error';

const PROTOCOL_RE = /^https?:\/\//;
const IMPLICIT_PROTOCOL_RE = /^\/\//;

export default class TestingUnit {
constructor (globals) {
this.globals = globals;

this.name = null;
this.pageUrl = null;
this.authCredentials = null;
this.only = false;
this.skip = false;

var unit = this;

this.apiOrigin = function apiOrigin (...args) {
return unit._add(...args);
};

delegateAPI(this, this.apiOrigin, this.constructor.API_LIST, null, false);
}

_add () {
throw new Error('Not implemented');
}

_only$getter () {
this.only = true;

return this.apiOrigin;
}

_skip$getter () {
this.skip = true;

return this.apiOrigin;
}

_page$ (url, ...rest) {
this.pageUrl = handleTagArgs(url, rest);

var urlType = typeof this.pageUrl;

if (urlType !== 'string')
throw new APIError('page', MESSAGE.pageIsNotAString, urlType);

if (!PROTOCOL_RE.test(this.pageUrl)) {
var protocol = IMPLICIT_PROTOCOL_RE.test(this.pageUrl) ? 'http:' : 'http://';

this.pageUrl = protocol + this.pageUrl;
}

return this.apiOrigin;
}

_httpAuth$ (credentials) {
assertObject('httpAuth', 'credentials', credentials);
assertString('httpAuth', 'credentials.username', credentials.username);
assertString('httpAuth', 'credentials.password', credentials.password);

if (credentials.domain)
assertString('httpAuth', 'credentials.domain', credentials.domain);
if (credentials.workstation)
assertString('httpAuth', 'credentials.workstation', credentials.workstation);

this.authCredentials = credentials;

return this.apiOrigin;
}

static _wrapTestFunction (fn) {
return async testRun => {
var result = null;
var controller = new TestController(testRun);
var markeredfn = clientFnTestRunTracker.addTrackingMarkerToFunction(testRun.id, fn);

clientFnTestRunTracker.ensureEnabled();

try {
result = await markeredfn(controller);
}
catch (err) {
throw processTestFnError(err);
}

// NOTE: check if the last command in the test
// function is missing the `await` keyword.
controller._checkForMissingAwait();

return result;
};
}

static _makeAPIListForChildClass (ChildClass) {
ChildClass.API_LIST = TestingUnit.API_LIST.concat(getDelegatedAPIList(ChildClass.prototype));
}
}

TestingUnit.API_LIST = getDelegatedAPIList(TestingUnit.prototype);


27 changes: 0 additions & 27 deletions src/api/globals/wrap-test-function.js

This file was deleted.

Loading

0 comments on commit 540c5a6

Please sign in to comment.