Skip to content

Commit

Permalink
Add js-expr assertion parameters support (close DevExpress#1715) (Dev…
Browse files Browse the repository at this point in the history
…Express#1719)

* Add js-expr assertion parameters support (close DevExpress#1715)

* fix error message

* fix remarks
  • Loading branch information
churkin authored and VasilyStrelyaev committed Aug 18, 2017
1 parent b86b8ef commit a157a3f
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 14 deletions.
7 changes: 7 additions & 0 deletions src/errors/test-run/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ export class ExternalAssertionLibraryError extends TestRunErrorBase {
}
}

export class AssertionExecutableArgumentError extends ActionArgumentErrorBase {
constructor (argumentName, argumentValue, errMsg) {
super(TYPE.assertionExecutableArgumentError, argumentName, argumentValue);

this.errMsg = errMsg;
}
}

// Action parameters errors
//--------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions src/errors/test-run/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,5 +242,11 @@ export default {

[TYPE.roleSwitchInRoleInitializerError]: err => markup(err, `
Role cannot be switched while another role is being initialized.
`),

[TYPE.assertionExecutableArgumentError]: err => markup(err, `
Cannot evaluate the "${err.actualValue}" expression in the "${err.argumentName}" parameter because of the following error:
${err.errMsg}
`)
};
3 changes: 2 additions & 1 deletion src/errors/test-run/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ export default {
externalAssertionLibraryError: 'externalAssertionLibraryError',
pageLoadError: 'pageLoadError',
windowDimensionsOverflowError: 'windowDimensionsOverflowError',
roleSwitchInRoleInitializerError: 'roleSwitchInRoleInitializerError'
roleSwitchInRoleInitializerError: 'roleSwitchInRoleInitializerError',
assertionExecutableArgumentError: 'assertionExecutableArgumentError'
};
13 changes: 4 additions & 9 deletions src/test-run/commands/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,17 @@ import {
import { ActionSelectorError, SetNativeDialogHandlerCodeWrongTypeError } from '../../errors/test-run';
import { APIError } from '../../errors/runtime';
import { ExecuteClientFunctionCommand, ExecuteSelectorCommand } from './observation';
import { executeSelectorExpression } from '../execute-js-expression';

const RAW_API_JS_EXPRESSION_TYPE = 'js-expr';

import { executeJsExpression } from '../execute-js-expression';
import { isJSExpression } from './utils';

// Initializers
function initSelector (name, val, skipVisibilityCheck) {
if (val instanceof ExecuteSelectorCommand)
return val;

try {
var isRawAPIJsExpression = val !== null && typeof val === 'object' && val.type === RAW_API_JS_EXPRESSION_TYPE &&
typeof val.value === 'string';

if (isRawAPIJsExpression)
val = executeSelectorExpression(val.value, skipVisibilityCheck);
if (isJSExpression(val))
val = executeJsExpression(val.value, skipVisibilityCheck);

var builder = new SelectorBuilder(val, { visibilityCheck: !skipVisibilityCheck }, { instantiation: 'Selector' });

Expand Down
25 changes: 22 additions & 3 deletions src/test-run/commands/assertion.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import TYPE from './type';
import Assignable from '../../utils/assignable';
import { AssertionOptions } from './options';
import { APIError } from '../../errors/runtime';
import { AssertionExecutableArgumentError } from '../../errors/test-run';
import { executeJsExpression } from '../execute-js-expression';
import { isJSExpression } from './utils';

import { stringArgument, actionOptions, nonEmptyStringArgument } from './validations/argument';

Expand All @@ -9,6 +13,21 @@ function initAssertionOptions (name, val) {
return new AssertionOptions(val, true);
}

//Initializers
function initAssertionParameter (name, val, skipVisibilityCheck) {
try {
if (isJSExpression(val))
val = executeJsExpression(val.value, skipVisibilityCheck);

return val;
}
catch (err) {
var msg = err.constructor === APIError ? err.rawMessage : err.message;

throw new AssertionExecutableArgumentError(name, val.value, msg);
}
}

// Commands
export default class AssertionCommand extends Assignable {
constructor (obj) {
Expand All @@ -29,9 +48,9 @@ export default class AssertionCommand extends Assignable {
_getAssignableProperties () {
return [
{ name: 'assertionType', type: nonEmptyStringArgument, required: true },
{ name: 'actual' },
{ name: 'expected' },
{ name: 'expected2' },
{ name: 'actual', init: initAssertionParameter },
{ name: 'expected', init: initAssertionParameter },
{ name: 'expected2', init: initAssertionParameter },
{ name: 'message', type: stringArgument },
{ name: 'options', type: actionOptions, init: initAssertionOptions, required: true }
];
Expand Down
7 changes: 7 additions & 0 deletions src/test-run/commands/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// -------------------------------------------------------------
import TYPE from './type';

const RAW_API_JS_EXPRESSION_TYPE = 'js-expr';

export function isCommandRejectableByPageError (command) {
return !isObservationCommand(command) && !isBrowserManipulationCommand(command) && !isServiceCommand(command) ||
isRejectablePrepareBrowserManipulationCommand(command)
Expand Down Expand Up @@ -70,3 +72,8 @@ export function isExecutableInTopWindowOnly (command) {
command.type === TYPE.hideAssertionRetriesStatus ||
command.type === TYPE.setBreakpoint;
}

export function isJSExpression (val) {
return val !== null && typeof val === 'object' && val.type === RAW_API_JS_EXPRESSION_TYPE &&
typeof val.value === 'string';
}
2 changes: 1 addition & 1 deletion src/test-run/execute-js-expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createContext, runInContext } from 'vm';
import SelectorBuilder from '../client-functions/selectors/selector-builder';
import ClientFunctionBuilder from '../client-functions/client-function-builder';

export function executeSelectorExpression (expression, skipVisibilityCheck) {
export function executeJsExpression (expression, skipVisibilityCheck) {
var sandbox = {
Selector: (fn, options = {}) => {
if (skipVisibilityCheck)
Expand Down
7 changes: 7 additions & 0 deletions test/functional/fixtures/api/raw/assertions/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,11 @@ describe('[Raw API] Assertions', function () {
expect(errs[0]).contains('[[Timeout option is string callsite]]');
});
});

it('Should process js expression', function () {
return runTests('./testcafe-fixtures/assertions.testcafe', 'js expression', {
shouldFail: false,
only: 'chrome'
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,17 @@
"callsite": "[[Timeout option is string callsite]]"
}
]
},
{
"name": "js expression",
"commands": [
{
"type": "assertion",
"assertionType": "eql",
"actual": { "type" : "js-expr", "value" : "Selector('body').count" },
"expected": 1
}
]
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Cannot evaluate the "1 + temp" expression in the "actual" parameter because
of the following error:

Unexpected identifier

Browser: Chrome 15.0.874 / Mac OS X 10.8.1
Screenshot: /unix/path/with/<tag>

18 |function func1 () {
19 | record = createCallsiteRecord({ byFunctionName: 'func1' });
20 |}
21 |
22 |(function func2 () {
> 23 | func1();
24 |})();
25 |
26 |stackTrace.filter.deattach(stackFilter);
27 |
28 |module.exports = record;

at func2 (testfile.js:23:5)
at Object.<anonymous> (testfile.js:24:3)
50 changes: 50 additions & 0 deletions test/server/test-run-commands-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,34 @@ describe('Test run commands', function () {
}
});
});

it('Should process js expression as an assertion parameter', function () {
var commandObj = {
type: TYPE.assertion,
assertionType: 'eql',

actual: {
type: 'js-expr',
value: '1 + 2'
},

expected: 1
};

var command = createCommand(commandObj);

expect(JSON.parse(JSON.stringify(command))).eql({
type: TYPE.assertion,
assertionType: 'eql',
actual: 3,
expected: 1,
message: null,

options: {
timeout: null
}
});
});
});

describe('Validation', function () {
Expand Down Expand Up @@ -2656,6 +2684,28 @@ describe('Test run commands', function () {
callsite: null
}
);

assertThrow(
function () {
return createCommand({
type: TYPE.assertion,
assertionType: 'ok',

actual: {
type: 'js-expr',
value: 'invalid js code'
}
});
},
{
isTestCafeError: true,
argumentName: 'actual',
actualValue: 'invalid js code',
errMsg: 'Unexpected identifier',
type: ERROR_TYPE.assertionExecutableArgumentError,
callsite: null
}
);
});

it('Should validate js expression as Selector', function () {
Expand Down
5 changes: 5 additions & 0 deletions test/server/test-run-error-formatting-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var TEST_RUN_PHASE = require('../../lib/test-
var TYPE = require('../../lib/errors/test-run/type');
var TestRunErrorFormattableAdapter = require('../../lib/errors/test-run/formattable-adapter');
var testCallsite = require('./data/test-callsite');
var AssertionExecutableArgumentError = require('../../lib/errors/test-run').AssertionExecutableArgumentError;
var ActionIntegerOptionError = require('../../lib/errors/test-run').ActionIntegerOptionError;
var ActionPositiveIntegerOptionError = require('../../lib/errors/test-run').ActionPositiveIntegerOptionError;
var ActionIntegerArgumentError = require('../../lib/errors/test-run').ActionIntegerArgumentError;
Expand Down Expand Up @@ -315,6 +316,10 @@ describe('Error formatting', function () {
it('Should format "actionRoleArgumentError"', function () {
assertErrorMessage('action-role-argument-error', new ActionRoleArgumentError('role', 'number'));
});

it('Should format "assertionExecutableArgumentError"', function () {
assertErrorMessage('assertion-executable-argument-error', new AssertionExecutableArgumentError('actual', '1 + temp', 'Unexpected identifier'));
});
});

describe('Test coverage', function () {
Expand Down

0 comments on commit a157a3f

Please sign in to comment.