@qavajs/cypress 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,58 @@
1
+ import { When } from '@qavajs/cypress-runner-adapter';
2
+ import { parseCoords } from './utils';
3
+
4
+ /**
5
+ * Press mouse key
6
+ * @param {string} button - button to press (left, right, middle)
7
+ * @example When I press left mouse button
8
+ */
9
+ When('I press {mouseButton} mouse button on {string}', function (buttons, coords) {
10
+ const [x, y] = parseCoords(this.value(coords));
11
+ cy.get('body')
12
+ .trigger('mouseenter', x, y)
13
+ .trigger('mouseover', x, y)
14
+ .trigger('mousemove', x, y)
15
+ .trigger('mousedown', x, y, { buttons });
16
+ });
17
+
18
+ /**
19
+ * Release mouse key
20
+ * @param {string} button - button to release (left, right, middle)
21
+ * @example When I release left mouse button
22
+ */
23
+ When('I release {mouseButton} mouse button on {string}', function (button, coords) {
24
+ const [x, y] = parseCoords(this.value(coords));
25
+ cy.get('body')
26
+ .trigger('mouseenter', x, y)
27
+ .trigger('mouseover', x, y)
28
+ .trigger('mousemove', x, y)
29
+ .trigger('mouseup', x, y, { button });
30
+ });
31
+
32
+ /**
33
+ * Move mouse to coordinates
34
+ * @param {string} coords - x, y coordinates to move
35
+ * @example When I move mouse to '10, 15'
36
+ */
37
+ When('I move mouse to {string}', function (coords){
38
+ const [x, y] = parseCoords(this.value(coords));
39
+ cy.get('body')
40
+ .trigger('mouseenter', x, y)
41
+ .trigger('mouseover', x, y)
42
+ .trigger('mousemove', x, y);
43
+ });
44
+
45
+ /**
46
+ * Scroll mouse wheel by x, y offset
47
+ * @param {string} coords - x, y offset to scroll
48
+ * @example When I scroll mouse wheel by '0, 15'
49
+ */
50
+ When('I scroll mouse wheel by {string} on {string}', function (offset, coords) {
51
+ const [x, y] = parseCoords(this.value(coords));
52
+ const [deltaX, deltaY] = parseCoords(this.value(offset));
53
+ cy.get('body')
54
+ .trigger('mouseenter', x, y)
55
+ .trigger('mouseover', x, y)
56
+ .trigger('mousemove', x, y)
57
+ .trigger('wheel', x, y, { deltaX, deltaY });
58
+ });
package/lib/setup.js ADDED
@@ -0,0 +1,46 @@
1
+ import {po} from '@qavajs/po-cypress';
2
+ import App from '../test-e2e/page_object';
3
+ import memory from '@qavajs/memory';
4
+ import Memory from '../test-e2e/memory';
5
+ import { setWorldConstructor } from '@qavajs/cypress-runner-adapter';
6
+
7
+ export class QavajsCypressWorld {
8
+
9
+ constructor() {
10
+ po.init(cy, { logger: { log: (value) => {
11
+ const [displayName, message] = value.split(/\s->\s/);
12
+ Cypress.log({
13
+ displayName,
14
+ message
15
+ })
16
+ }
17
+ }
18
+ });
19
+ po.register(window.pageObject);
20
+ memory.register(window.memory);
21
+ memory.setLogger({ log: (value) => {
22
+ const [displayName, message] = value.split(/\s->\s/);
23
+ Cypress.log({
24
+ displayName,
25
+ message
26
+ })
27
+ }
28
+ });
29
+ this.po = po;
30
+ this.memory = memory;
31
+ }
32
+
33
+ element(alias) {
34
+ return this.po.getElement(this.memory.getValue(alias));
35
+ }
36
+
37
+ value(expression) {
38
+ return this.memory.getValue(expression);
39
+ }
40
+
41
+ setValue(key, value) {
42
+ this.memory.setValue(key, value);
43
+ }
44
+ }
45
+
46
+ setWorldConstructor(QavajsCypressWorld);
package/lib/storage.js ADDED
@@ -0,0 +1,59 @@
1
+ import { When } from '@qavajs/cypress-runner-adapter';
2
+
3
+ /**
4
+ * Set cookie
5
+ * @param {string} cookie - cookie name
6
+ * @param {string} value - value to set
7
+ * @example I set 'userID' cookie 'user1'
8
+ * @example I set 'userID' cookie '$userIdCookie'
9
+ */
10
+ When('I set {string} cookie as {string}', function (cookie, value) {
11
+ const cookieValue = this.value(value);
12
+ const cookieObject = typeof cookieValue === 'object' ? cookieValue : { value: cookieValue };
13
+ cy.setCookie(cookie, cookieObject.value, cookieObject);
14
+ });
15
+
16
+ /**
17
+ * Save cookie value to memory
18
+ * @param {string} cookie - cookie name
19
+ * @param {string} key - memory key
20
+ * @example I save value of 'auth' cookie as 'authCookie'
21
+ */
22
+ When('I save value of {string} cookie as {string}', function (cookie, key) {
23
+ const cookieName = this.value(cookie);
24
+ cy.getCookie(cookieName).then((cookie) => {
25
+ this.setValue(key, cookie);
26
+ });
27
+ });
28
+
29
+
30
+ /**
31
+ * Set value of local/session storage
32
+ * @param {string} storageKey - local/session storage key to set value
33
+ * @param {string} storageType - storage type (local or session)
34
+ * @param {string} value - value to set
35
+ * @example I set 'username' local storage value as 'user1'
36
+ * @example I set '$sessionStorageKey' session storage value as '$sessionStorageValue'
37
+ */
38
+ When('I set {string} {word} storage value as {string}', function (storageKey, storageType, value) {
39
+ const resolvedValue = this.value(value);
40
+ cy.window().then((win) => {
41
+ console.log(win)
42
+ win[storageType + 'Storage'].setItem(storageKey, resolvedValue);
43
+ });
44
+ });
45
+
46
+ /**
47
+ * Save value of local/session storage to memory
48
+ * @param {string} storageKey - local/session storage key to set value
49
+ * @param {string} storageType - storage type (local or session)
50
+ * @param {string} key - memory key
51
+ * @example I save value of 'username' local storage as 'localStorageValue'
52
+ * @example I save value of '$sessionStorageKey' session storage value as 'sessionStorageValue'
53
+ */
54
+ When('I save value of {string} {word} storage as {string}', function (storageKey, storageType, key) {
55
+ const resolvedStorageKey = this.value(storageKey);
56
+ cy.window().then((win) => {
57
+ this.setValue(key, win[storageType + 'Storage'].getItem(resolvedStorageKey));
58
+ });
59
+ });
package/lib/types.js ADDED
@@ -0,0 +1,32 @@
1
+ import { defineParameterType } from '@qavajs/cypress-runner-adapter';
2
+
3
+ defineParameterType({
4
+ name: 'validation',
5
+ regexp: /((?:is |do |does |to )?(not |to not )?(?:to )?(?:be )?(equal|strictly equal|deeply equal|have member|match|contain|above|below|greater than|less than|have type)(?:s|es)?)/,
6
+ useForSnippets: false
7
+ });
8
+
9
+ defineParameterType({
10
+ name: 'conditionExpect',
11
+ regexp: /((not )?to (?:be )?(present|clickable|visible|invisible|enabled|disabled|in viewport))/,
12
+ useForSnippets: false
13
+ });
14
+
15
+ defineParameterType({
16
+ name: 'poType',
17
+ regexp: /(element|collection)/,
18
+ useForSnippets: false
19
+ });
20
+
21
+ defineParameterType({
22
+ name: 'mouseButton',
23
+ regexp: /(left|right|middle)/,
24
+ transformer: button => ({ left: 1, right: 2, middle: 4 }[button]),
25
+ useForSnippets: false
26
+ });
27
+
28
+ defineParameterType({
29
+ name: 'browserButton',
30
+ regexp: /(back|forward)/,
31
+ useForSnippets: false
32
+ });
package/lib/utils.js ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Parse 'x, y' string to coordinates array
3
+ * @param {string} coords - 'x, y' string
4
+ * @return {number[]} - coords array
5
+ */
6
+ export function parseCoords(coords) {
7
+ return coords.split(/\s?,\s?/).map(c => parseFloat(c ?? 0))
8
+ }
9
+
10
+ export function equalOrIncludes(value, argument) {
11
+ return Array.isArray(value)
12
+ ? value.includes(argument)
13
+ : value === argument;
14
+ }
15
+
16
+ export async function throwTimeoutError(fn, message) {
17
+ try {
18
+ await fn()
19
+ } catch (err) {
20
+ if (err.message.includes('exceeded while waiting on the predicate')) {
21
+ throw new Error(message);
22
+ }
23
+ throw err
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Transform key-value data table to JS object
29
+ * @param ctx
30
+ * @param dataTable
31
+ * @return {Object}
32
+ */
33
+ export async function dataTable2Object(ctx, dataTable) {
34
+ const obj = {};
35
+ for (const [key, value] of dataTable.raw()) {
36
+ obj[key] = await ctx.value(value);
37
+ }
38
+ return obj;
39
+ }
40
+
41
+ /**
42
+ * Transform key-value data table to array
43
+ * @param ctx
44
+ * @param dataTable
45
+ * @return {any[]}
46
+ */
47
+ export function dataTable2Array(ctx, dataTable) {
48
+ return dataTable.raw().map(([value]) => ctx.value(value));
49
+ }
@@ -0,0 +1,259 @@
1
+ import { Then } from '@qavajs/cypress-runner-adapter';
2
+ import { getValidation } from './valueExpect';
3
+ import { getConditionExpect } from './conditionExpect';
4
+
5
+ /**
6
+ * Verify element condition
7
+ * @param {string} alias - element to wait condition
8
+ * @param {string} condition - wait condition
9
+ * @example I expect 'Header' to be visible
10
+ * @example I expect 'Loading' not to be present
11
+ * @example I expect 'Search Bar > Submit Button' to be clickable
12
+ */
13
+ Then('I expect {string} {conditionExpect}', function (alias, condition) {
14
+ const chainer = getConditionExpect(condition);
15
+ const element = this.element(alias);
16
+ element.should(chainer)
17
+ });
18
+
19
+ /**
20
+ * Verify that text of element satisfies condition
21
+ * @param {string} alias - element to get text
22
+ * @param {string} validationType - validation
23
+ * @param {string} value - expected result
24
+ * @example I expect text of '#1 of Search Results' equals to 'google'
25
+ * @example I expect text of '#2 of Search Results' does not contain 'yandex'
26
+ */
27
+ Then(
28
+ 'I expect text of {string} {validation} {string}',
29
+ function (alias, validationType, value) {
30
+ const expectedValue = this.value(value);
31
+ const element = this.element(alias);
32
+ const validation = getValidation(validationType);
33
+ element.should((e) => {
34
+ validation(e.text(), expectedValue);
35
+ });
36
+ }
37
+ );
38
+
39
+ /**
40
+ * Verify that value of element satisfies condition
41
+ * @param {string} alias - element to get text
42
+ * @param {string} validationType - validation
43
+ * @param {string} value - expected result
44
+ * @example I expect value of '#1 of Search Results' equals to 'google'
45
+ * @example I expect value of '#2 of Search Results' does not contain 'yandex'
46
+ */
47
+ Then(
48
+ 'I expect value of {string} {validation} {string}',
49
+ function (alias, validationType, value) {
50
+ const expectedValue = this.value(value);
51
+ const element = this.element(alias);
52
+ const validation = getValidation(validationType);
53
+ element.should((e) => {
54
+ validation(e.val(), expectedValue);
55
+ });
56
+ }
57
+ );
58
+
59
+ /**
60
+ * Verify that property of element satisfies condition
61
+ * @param {string} property - element to verify
62
+ * @param {string} alias - element to verify
63
+ * @param {string} validationType - validation
64
+ * @param {string} value - expected value
65
+ * @example I expect 'value' property of 'Search Input' to be equal 'text'
66
+ * @example I expect 'innerHTML' property of 'Label' to contain '<b>'
67
+ */
68
+ Then(
69
+ 'I expect {string} property of {string} {validation} {string}',
70
+ function (property, alias, validationType, value) {
71
+ const propertyName = this.value(property);
72
+ const expectedValue = this.value(value);
73
+ const element = this.element(alias);
74
+ const validation = getValidation(validationType);
75
+ element.should((e) => {
76
+ validation(e.prop(propertyName), expectedValue);
77
+ });
78
+ }
79
+ );
80
+
81
+ /**
82
+ * Verify that attribute of element satisfies condition
83
+ * @param {string} attribute - element to verify
84
+ * @param {string} alias - element to verify
85
+ * @param {string} validationType - validation
86
+ * @param {string} value - expected value
87
+ * @example I expect 'href' attribute of 'Home Link' to contain '/home'
88
+ */
89
+ Then(
90
+ 'I expect {string} attribute of {string} {validation} {string}',
91
+ function (attribute, alias, validationType, value) {
92
+ const attributeName = this.value(attribute);
93
+ const expectedValue = this.value(value);
94
+ const element = this.element(alias);
95
+ const validation = getValidation(validationType);
96
+ element.should((e) => {
97
+ validation(e.attr(attributeName), expectedValue);
98
+ });
99
+ }
100
+ );
101
+
102
+ /**
103
+ * Verify that current url satisfies condition
104
+ * @param {string} validationType - validation
105
+ * @param {string} expected - expected value
106
+ * @example I expect current url contains 'wikipedia'
107
+ * @example I expect current url equals 'https://wikipedia.org'
108
+ */
109
+ Then(
110
+ 'I expect current url {validation} {string}',
111
+ function (validationType, expected) {
112
+ const validation = getValidation(validationType);
113
+ const expectedUrl = this.value(expected);
114
+ cy.url().should((actualUrl) => {
115
+ validation(actualUrl, expectedUrl);
116
+ });
117
+ }
118
+ );
119
+
120
+ /**
121
+ * Verify that number of element in collection satisfies condition
122
+ * @param {string} alias - collection to verify
123
+ * @param {string} validationType - validation
124
+ * @param {string} value - expected value
125
+ * @example I expect number of elements in 'Search Results' collection to be equal '50'
126
+ * @example I expect number of elements in 'Search Results' collection to be above '49'
127
+ * @example I expect number of elements in 'Search Results' collection to be below '51'
128
+ */
129
+ Then(
130
+ 'I expect number of elements in {string} collection {validation} {string}',
131
+ function (alias, validationType, value) {
132
+ const expectedValue = this.value(value);
133
+ const collection = this.element(alias);
134
+ const validation = getValidation(validationType);
135
+ collection.should((collection) => {
136
+ validation(collection.length, expectedValue);
137
+ });
138
+ }
139
+ );
140
+
141
+ /**
142
+ * Verify that page title satisfies condition
143
+ * @param {string} validationType - validation
144
+ * @param {string} expected - expected value
145
+ * @example I expect page title equals 'Wikipedia'
146
+ */
147
+ Then(
148
+ 'I expect page title {validation} {string}',
149
+ function (validationType, expected) {
150
+ const validation = getValidation(validationType);
151
+ const expectedTitle = this.value(expected);
152
+ cy.title().should((actualTitle) => {
153
+ validation(actualTitle, expectedTitle);
154
+ });
155
+ }
156
+ );
157
+
158
+ /**
159
+ * Verify that all texts in collection satisfy condition
160
+ * @param {string} alias - collection to get texts
161
+ * @param {string} validationType - validation
162
+ * @param {string} value - expected result
163
+ * @example I expect text of every element in 'Search Results' collection equals to 'google'
164
+ * @example I expect text of every element in 'Search Results' collection does not contain 'yandex'
165
+ */
166
+ Then(
167
+ 'I expect text of every element in {string} collection {validation} {string}',
168
+ function (alias, validationType, value) {
169
+ const expectedValue = this.value(value);
170
+ const collection = this.element(alias);
171
+ const validation = getValidation(validationType);
172
+ collection.each((element) => {
173
+ validation(element.text(), expectedValue);
174
+ });
175
+ }
176
+ );
177
+
178
+ /**
179
+ * Verify that all particular attributes in collection satisfy condition
180
+ * @param {string} alias - collection to get attrs
181
+ * @param {string} validationType - validation
182
+ * @param {string} value - expected result
183
+ * @example I expect 'href' attribute of every element in 'Search Results' collection to contain 'google'
184
+ */
185
+ Then(
186
+ 'I expect {string} attribute of every element in {string} collection {validation} {string}',
187
+ function (attribute, alias, validationType, value) {
188
+ const expectedValue = this.value(value);
189
+ const collection = this.element(alias);
190
+ const validation = getValidation(validationType);
191
+ collection.each((element) => {
192
+ validation(element.attr(attribute), expectedValue);
193
+ });
194
+ }
195
+ );
196
+
197
+ /**
198
+ * Verify that all particular properties in collection satisfy condition
199
+ * @param {string} alias - collection to get props
200
+ * @param {string} validationType - validation
201
+ * @param {string} value - expected result
202
+ * @example I expect 'href' property of every element in 'Search Results' collection to contain 'google'
203
+ */
204
+ Then(
205
+ 'I expect {string} property of every element in {string} collection {validation} {string}',
206
+ function (property, alias, validationType, value) {
207
+ const expectedValue = this.value(value);
208
+ const collection = this.element(alias);
209
+ const validation = getValidation(validationType);
210
+ collection.each((element) => {
211
+ validation(element.prop(property), expectedValue);
212
+ });
213
+ }
214
+ );
215
+
216
+ /**
217
+ * Verify that css property of element satisfies condition
218
+ * @param {string} property - element to verify
219
+ * @param {string} alias - element to verify
220
+ * @param {string} validationType - validation
221
+ * @param {string} value - expected value
222
+ * @example I expect 'color' css property of 'Search Input' to be equal 'rgb(42, 42, 42)'
223
+ * @example I expect 'font-family' css property of 'Label' to contain 'Fira'
224
+ */
225
+ Then(
226
+ 'I expect {string} css property of {string} {validation} {string}',
227
+ function (property, alias, validationType, value) {
228
+ const propertyName = this.value(property);
229
+ const expectedValue = this.value(value);
230
+ const element = this.element(alias);
231
+ const validation = getValidation(validationType);
232
+ element.should((e) => {
233
+ validation(e.css(propertyName), expectedValue);
234
+ });
235
+ }
236
+ );
237
+
238
+ /**
239
+ * Verify that text of an alert meets expectation
240
+ * @param {string} validationType - validation
241
+ * @param {string} value - expected text value
242
+ * @example I expect text of alert does not contain 'coffee'
243
+ */
244
+ Then(
245
+ 'I expect text of alert {validation} {string}',
246
+ function (validationType, expectedValue) {
247
+ const validation = getValidation(validationType);
248
+ const alertHandler = new Cypress.Promise((resolve, reject) => {
249
+ cy.on('window:alert', (alertText)=> {
250
+ resolve(alertText)
251
+ });
252
+ cy.on('window:confirm', (alertText)=> {
253
+ resolve(alertText)
254
+ });
255
+ });
256
+ return alertHandler.then(alertText => { validation(alertText, expectedValue) })
257
+ }
258
+ );
259
+
@@ -0,0 +1,84 @@
1
+ chai.Assertion.addMethod('notStrictEqual', function (ER) {
2
+ const obj = this._obj;
3
+
4
+ this.assert(
5
+ obj == ER,
6
+ 'expected #{this} to equal #{exp}',
7
+ 'expected #{this} to not equal #{exp}',
8
+ ER,
9
+ obj
10
+ );
11
+ });
12
+
13
+ export const validations = {
14
+ EQUAL: 'equal',
15
+ DEEPLY_EQUAL: 'deeply equal',
16
+ STRICTLY_EQUAL: 'strictly equal',
17
+ HAVE_MEMBERS: 'have member',
18
+ MATCH: 'match',
19
+ CONTAIN: 'contain',
20
+ ABOVE: 'above',
21
+ BELOW: 'below',
22
+ GREATER: 'greater than',
23
+ LESS: 'less than',
24
+ HAVE_TYPE: 'have type',
25
+ INCLUDE_MEMBERS: 'include member',
26
+ HAVE_PROPERTY: 'have property'
27
+ };
28
+
29
+ const isClause = '(?:is |do |does |to )?';
30
+ const notClause = '(?<reverse>not |to not )?';
31
+ const toBeClause = '(?:to )?(?:be )?';
32
+ const validationClause = `(?:(?<validation>${Object.values(validations).join('|')})(?:s|es)?)`;
33
+
34
+ export const validationExtractRegexp = new RegExp(`^${isClause}${notClause}${toBeClause}${validationClause}$`);
35
+ export const validationRegexp = new RegExp(`(${isClause}${notClause}${toBeClause}${validationClause})`);
36
+
37
+ const aboveFn = (expectClause, ER) => expectClause.above(toNumber(ER));
38
+ const belowFn = (expectClause, ER) => expectClause.below(toNumber(ER));
39
+ const validationFns = {
40
+ [validations.EQUAL]: (expectClause, ER) => expectClause.notStrictEqual(ER),
41
+ [validations.STRICTLY_EQUAL]: (expectClause, ER) => expectClause.equal(ER),
42
+ [validations.DEEPLY_EQUAL]: (expectClause, ER) => expectClause.eql(ER),
43
+ [validations.HAVE_MEMBERS]: (expectClause, ER) => expectClause.have.members(ER),
44
+ [validations.MATCH]: (expectClause, ER) => expectClause.match(toRegexp(ER)),
45
+ [validations.CONTAIN]: (expectClause, ER) => expectClause.contain(ER),
46
+ [validations.ABOVE]: aboveFn,
47
+ [validations.BELOW]: belowFn,
48
+ [validations.GREATER]: aboveFn,
49
+ [validations.LESS]: belowFn,
50
+ [validations.HAVE_TYPE]: (expectClause, ER) => expectClause.a(ER),
51
+ [validations.INCLUDE_MEMBERS]: (expectClause, ER) => expectClause.include.members(ER),
52
+ [validations.HAVE_PROPERTY]: (expectClause, ER) => expectClause.have.property(ER),
53
+ };
54
+
55
+ /**
56
+ * Basic verification function
57
+ * @param {Object} object with all needed data for validation
58
+ */
59
+ export function verify({ AR, ER, validation, reverse }) {
60
+ const expectClause = reverse ? expect(AR).to.not : expect(AR).to;
61
+ const validate = validationFns[validation];
62
+ validate(expectClause, ER);
63
+ }
64
+
65
+ export function getValidation(validationType) {
66
+ const match = validationExtractRegexp.exec(validationType);
67
+ if (!match) throw new Error(`validation '${validationType}' is not supported`);
68
+ const { reverse, validation } = match.groups;
69
+ return function (AR, ER) {
70
+ verify({ AR, ER, validation, reverse: Boolean(reverse) });
71
+ };
72
+ }
73
+
74
+ function toNumber(n) {
75
+ const parsedNumber = parseFloat(n);
76
+ if (Number.isNaN(parsedNumber)) {
77
+ throw new Error(`${n} is not a number`);
78
+ }
79
+ return parsedNumber
80
+ }
81
+
82
+ function toRegexp(r) {
83
+ return r instanceof RegExp ? r : RegExp(r)
84
+ }