system-testing 1.0.23 → 1.0.25

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.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # System testing
2
+
3
+ Rails inspired system testing for Expo apps.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install --save-dev system-testing
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```js
14
+ import retry from "awaitery/src/retry.js"
15
+ import SystemTest from "system-testing/src/system-test.js"
16
+ import wait from "awaitery/src/wait.js"
17
+ import waitFor from "awaitery/src/wait-for.js"
18
+
19
+ import createUser from "@/src/testing/create-user.js"
20
+ import initialize from "@/src/initialize"
21
+ import Option from "@/src/models/option"
22
+
23
+ describe("Sign in page", () => {
24
+ test("it navigates to the sign in page and signs in", async () => {
25
+ await initialize()
26
+
27
+ await SystemTest.run(async (systemTest) => {
28
+ await createUser(userAttributes)
29
+
30
+ await systemTest.visit("/")
31
+ await systemTest.findByTestID("frontpageScreen", {useBaseSelector: false})
32
+ await wait(250)
33
+
34
+ await retry(async () => {
35
+ await systemTest.click("[data-testid='signInButton']")
36
+ await systemTest.findByTestID("app/sign-in")
37
+ })
38
+
39
+ await systemTest.interact("[data-testid='signInEmailInput']", "sendKeys", "user@example.com")
40
+ await systemTest.interact("[data-testid='signInPasswordInput']", "sendKeys", "password")
41
+
42
+ const emailInputValue = await systemTest.interact("[data-testid='signInEmailInput']", "getAttribute", "value")
43
+ const passwordInputValue = await systemTest.interact("[data-testid='signInPasswordInput']", "getAttribute", "value")
44
+
45
+ expect(emailInputValue).toEqual("user@example.com")
46
+ expect(passwordInputValue).toEqual("password")
47
+
48
+ await systemTest.click("[data-testid='signInSubmitButton']")
49
+ await systemTest.expectNotificationMessage("You were signed in.")
50
+
51
+ await waitFor(async () => {
52
+ const optionUserID = await Option.findBy({key: "userID"})
53
+
54
+ if (!optionUserID) {
55
+ throw new Error("Option for user ID didn't exist")
56
+ }
57
+
58
+ expect(optionUserID.value()).toEqual("805")
59
+ })
60
+ })
61
+ })
62
+ })
63
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "system-testing",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "System testing with Selenium and browsers.",
5
5
  "keywords": [
6
6
  "system",
@@ -151,14 +151,31 @@ export default class SystemTest {
151
151
  *
152
152
  * @param {import("selenium-webdriver").WebElement} element
153
153
  **/
154
- async click(element) {
155
- if (typeof element == "string") {
156
- element = await this.find(element)
157
- }
154
+ async click(elementOrIdentifier) {
155
+ let tries = 0
158
156
 
159
- const actions = this.driver.actions({async: true})
157
+ while (true) {
158
+ tries++
160
159
 
161
- await actions.move({origin: element}).click().perform()
160
+ try {
161
+ const element = await this._findElement(elementOrIdentifier)
162
+ const actions = this.driver.actions({async: true})
163
+
164
+ await actions.move({origin: element}).click().perform()
165
+ break
166
+ } catch (error) {
167
+ if (error.constructor.name === "ElementNotInteractableError") {
168
+ if (tries >= 3) {
169
+ throw new Error(`Element ${element.constructor.name} click failed after ${tries} tries - ${error.constructor.name}: ${error.message}`)
170
+ } else {
171
+ await wait(50)
172
+ }
173
+ } else {
174
+ // Re-throw with un-corrupted stack trace
175
+ throw new Error(`Element ${element.constructor.name} click failed - ${error.constructor.name}: ${error.message}`)
176
+ }
177
+ }
178
+ }
162
179
  }
163
180
 
164
181
  /**
@@ -198,6 +215,18 @@ export default class SystemTest {
198
215
  */
199
216
  async findByTestID(testID, args) { return await this.find(`[data-testid='${testID}']`, args) }
200
217
 
218
+ async _findElement(elementOrIdentifier) {
219
+ let element
220
+
221
+ if (typeof elementOrIdentifier == "string") {
222
+ element = await this.find(elementOrIdentifier)
223
+ } else {
224
+ element = elementOrIdentifier
225
+ }
226
+
227
+ return element
228
+ }
229
+
201
230
  /**
202
231
  * Finds a single element by CSS selector without waiting
203
232
  *
@@ -256,20 +285,17 @@ export default class SystemTest {
256
285
  * @returns {Promise<any>}
257
286
  */
258
287
  async interact(elementOrIdentifier, methodName, ...args) {
259
- let element
260
288
  let tries = 0
261
289
 
262
290
  while (true) {
263
291
  tries++
264
292
 
265
- if (typeof elementOrIdentifier == "string") {
266
- element = await this.find(elementOrIdentifier)
267
- } else {
268
- element = elementOrIdentifier
269
- }
293
+ const element = await this._findElement(elementOrIdentifier)
270
294
 
271
295
  if (!element[methodName]) {
272
- // throw new Error(`${element.constructor.name} has no method named: ${methodName}`)
296
+ throw new Error(`${element.constructor.name} hasn't an attribute named: ${methodName}`)
297
+ } else if (typeof element[methodName] != "function") {
298
+ throw new Error(`${element.constructor.name}#${methodName} is not a function`)
273
299
  }
274
300
 
275
301
  try {
@@ -278,9 +304,17 @@ export default class SystemTest {
278
304
  if (error.constructor.name === "ElementNotInteractableError") {
279
305
  // Retry finding the element and interacting with it
280
306
  if (tries >= 3) {
281
- throw new Error(`${element.constructor.name} ${methodName} failed after ${tries} tries - ${error.constructor.name}: ${error.message}`)
307
+ let elementDescription
308
+
309
+ if (typeof elementOrIdentifier == "string") {
310
+ elementDescription = `CSS selector ${elementOrIdentifier}`
311
+ } else {
312
+ elementDescription = `${element.constructor.name}`
313
+ }
314
+
315
+ throw new Error(`${elementDescription} ${methodName} failed after ${tries} tries - ${error.constructor.name}: ${error.message}`)
282
316
  } else {
283
- await wait(100)
317
+ await wait(50)
284
318
  }
285
319
  } else {
286
320
  // Re-throw with un-corrupted stack trace
@@ -302,7 +336,7 @@ export default class SystemTest {
302
336
  await this.findNoWait(selector)
303
337
  found = true
304
338
  } catch (error) {
305
- if (!error.message.startsWith("Element couldn't be found by CSS:")) {
339
+ if (!error.message.startsWith("Element couldn't be found after ")) {
306
340
  throw error
307
341
  }
308
342
  }