@sdods/cli 0.2.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.
- package/LICENSE +17 -0
- package/bin/sdods.js +19 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +11 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.js +219 -0
- package/dist/commands/analyze.d.ts +20 -0
- package/dist/commands/analyze.js +216 -0
- package/dist/commands/auth.d.ts +7 -0
- package/dist/commands/auth.js +97 -0
- package/dist/commands/browsers.d.ts +17 -0
- package/dist/commands/browsers.js +103 -0
- package/dist/commands/completion.d.ts +17 -0
- package/dist/commands/completion.js +143 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.js +79 -0
- package/dist/commands/coverage.d.ts +3 -0
- package/dist/commands/coverage.js +65 -0
- package/dist/commands/data.d.ts +7 -0
- package/dist/commands/data.js +188 -0
- package/dist/commands/db.d.ts +3 -0
- package/dist/commands/db.js +241 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.js +323 -0
- package/dist/commands/env.d.ts +3 -0
- package/dist/commands/env.js +65 -0
- package/dist/commands/features.d.ts +3 -0
- package/dist/commands/features.js +57 -0
- package/dist/commands/feedback.d.ts +7 -0
- package/dist/commands/feedback.js +96 -0
- package/dist/commands/har.d.ts +3 -0
- package/dist/commands/har.js +124 -0
- package/dist/commands/heal.d.ts +4 -0
- package/dist/commands/heal.js +63 -0
- package/dist/commands/init.d.ts +38 -0
- package/dist/commands/init.js +363 -0
- package/dist/commands/insights.d.ts +7 -0
- package/dist/commands/insights.js +127 -0
- package/dist/commands/integrations.d.ts +14 -0
- package/dist/commands/integrations.js +306 -0
- package/dist/commands/lint.d.ts +3 -0
- package/dist/commands/lint.js +74 -0
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.js +190 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.js +133 -0
- package/dist/commands/proposals.d.ts +3 -0
- package/dist/commands/proposals.js +106 -0
- package/dist/commands/record.d.ts +3 -0
- package/dist/commands/record.js +174 -0
- package/dist/commands/report.d.ts +3 -0
- package/dist/commands/report.js +255 -0
- package/dist/commands/run.d.ts +41 -0
- package/dist/commands/run.js +369 -0
- package/dist/commands/schedule.d.ts +3 -0
- package/dist/commands/schedule.js +337 -0
- package/dist/commands/serve.d.ts +3 -0
- package/dist/commands/serve.js +38 -0
- package/dist/commands/steps.d.ts +8 -0
- package/dist/commands/steps.js +67 -0
- package/dist/commands/tokens.d.ts +3 -0
- package/dist/commands/tokens.js +110 -0
- package/dist/commands/trace.d.ts +10 -0
- package/dist/commands/trace.js +104 -0
- package/dist/commands/upgrade.d.ts +14 -0
- package/dist/commands/upgrade.js +115 -0
- package/dist/commands/users.d.ts +3 -0
- package/dist/commands/users.js +183 -0
- package/dist/commands/watch.d.ts +7 -0
- package/dist/commands/watch.js +116 -0
- package/dist/commands/workspace.d.ts +8 -0
- package/dist/commands/workspace.js +83 -0
- package/dist/context.d.ts +17 -0
- package/dist/context.js +22 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/program.d.ts +7 -0
- package/dist/program.js +90 -0
- package/dist/templates/project.d.ts +10 -0
- package/dist/templates/project.js +115 -0
- package/dist/ui.d.ts +16 -0
- package/dist/ui.js +67 -0
- package/package.json +61 -0
- package/templates/.claude/skills/sdods-record/SKILL.md +75 -0
- package/templates/.claude/skills/sdods-run/SKILL.md +54 -0
- package/templates/.env.example +62 -0
- package/templates/docker-compose.yml +40 -0
- package/templates/projects/demo-shop/.env.example +4 -0
- package/templates/projects/demo-shop/data/common/posts.yaml +11 -0
- package/templates/projects/demo-shop/data/common/products.json +8 -0
- package/templates/projects/demo-shop/data/common/users.csv +7 -0
- package/templates/projects/demo-shop/data/factories.ts +16 -0
- package/templates/projects/demo-shop/data/staging/users.csv +6 -0
- package/templates/projects/demo-shop/envs/local.yaml +17 -0
- package/templates/projects/demo-shop/envs/staging.yaml +23 -0
- package/templates/projects/demo-shop/features/__screenshots__/demo-shop--ui--chromium/darwin/inventory.png +0 -0
- package/templates/projects/demo-shop/features/api/posts.feature +90 -0
- package/templates/projects/demo-shop/features/auth/data-driven-login.feature +17 -0
- package/templates/projects/demo-shop/features/auth/login.feature +32 -0
- package/templates/projects/demo-shop/features/auth/user-pool.feature +20 -0
- package/templates/projects/demo-shop/features/cart/cart.feature +33 -0
- package/templates/projects/demo-shop/features/hybrid/seed-then-verify.feature +28 -0
- package/templates/projects/demo-shop/features/inventory/inventory.feature +38 -0
- package/templates/projects/demo-shop/features/inventory/network-intercept.feature +29 -0
- package/templates/projects/demo-shop/features/inventory/visual.feature +10 -0
- package/templates/projects/demo-shop/har/staging/cart.har +1 -0
- package/templates/projects/demo-shop/har/staging/cart.har.json +11 -0
- package/templates/projects/demo-shop/har/staging/login.har +1 -0
- package/templates/projects/demo-shop/har/staging/login.har.json +11 -0
- package/templates/projects/demo-shop/har/staging/posts.api.har +2061 -0
- package/templates/projects/demo-shop/har/staging/posts.har.json +8 -0
- package/templates/projects/demo-shop/har/staging/products.har +1 -0
- package/templates/projects/demo-shop/har/staging/products.har.json +11 -0
- package/templates/projects/demo-shop/har/staging/site.api.har +148 -0
- package/templates/projects/demo-shop/har/staging/site.har +1 -0
- package/templates/projects/demo-shop/har/staging/site.har.json +11 -0
- package/templates/projects/demo-shop/pages/CartPage.ts +20 -0
- package/templates/projects/demo-shop/pages/InventoryPage.ts +103 -0
- package/templates/projects/demo-shop/pages/LoginPage.ts +42 -0
- package/templates/projects/demo-shop/schemas/post.schema.json +13 -0
- package/templates/projects/demo-shop/sdods.project.yaml +185 -0
- package/templates/projects/demo-shop/steps/auth.ts +7 -0
- package/templates/projects/demo-shop/steps/fixtures.ts +39 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
id,username,password,role,displayName
|
|
2
|
+
1,standard_user,${DEMO_SHOP_PASSWORD:-secret_sauce},standard,Standard user
|
|
3
|
+
2,problem_user,${DEMO_SHOP_PASSWORD:-secret_sauce},problem,Problem user
|
|
4
|
+
3,performance_glitch_user,${DEMO_SHOP_PASSWORD:-secret_sauce},performance,Performance glitch user
|
|
5
|
+
4,locked_out_user,${DEMO_SHOP_PASSWORD:-secret_sauce},locked,Locked out user
|
|
6
|
+
5,error_user,${DEMO_SHOP_PASSWORD:-secret_sauce},standard,Error user
|
|
7
|
+
6,visual_user,${DEMO_SHOP_PASSWORD:-secret_sauce},standard,Visual user
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineFactories } from '@sdods/core/data';
|
|
2
|
+
|
|
3
|
+
/** Faker factories, seeded per scenario fingerprint so retries regenerate identical data. */
|
|
4
|
+
export default defineFactories({
|
|
5
|
+
user: (f) => ({
|
|
6
|
+
email: f.internet.email(),
|
|
7
|
+
firstName: f.person.firstName(),
|
|
8
|
+
lastName: f.person.lastName(),
|
|
9
|
+
zip: f.location.zipCode('#####'),
|
|
10
|
+
}),
|
|
11
|
+
post: (f) => ({
|
|
12
|
+
title: `SDODS ${f.lorem.words(3)}`,
|
|
13
|
+
body: f.lorem.sentence(),
|
|
14
|
+
userId: f.number.int({ min: 1, max: 10 }),
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
id,username,password,role,displayName
|
|
2
|
+
1,standard_user,${DEMO_SHOP_PASSWORD:-secret_sauce},standard,Standard user (staging)
|
|
3
|
+
2,problem_user,${DEMO_SHOP_PASSWORD:-secret_sauce},problem,Problem user (staging)
|
|
4
|
+
3,performance_glitch_user,${DEMO_SHOP_PASSWORD:-secret_sauce},performance,Performance glitch user (staging)
|
|
5
|
+
4,locked_out_user,${DEMO_SHOP_PASSWORD:-secret_sauce},locked,Locked out user (staging)
|
|
6
|
+
5,visual_user,${DEMO_SHOP_PASSWORD:-secret_sauce},standard,Visual user (staging)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: local
|
|
2
|
+
description: Same public sandboxes, smaller pool, headed-friendly timeouts. Override URLs via SDODS_UI_BASE_URL / SDODS_API_BASE_URL.
|
|
3
|
+
ui:
|
|
4
|
+
baseUrl: https://www.saucedemo.com
|
|
5
|
+
api:
|
|
6
|
+
baseUrl: https://jsonplaceholder.typicode.com
|
|
7
|
+
headers: { Accept: application/json }
|
|
8
|
+
auth: { type: none }
|
|
9
|
+
users:
|
|
10
|
+
poolSize: 2
|
|
11
|
+
vars:
|
|
12
|
+
defaultProduct: Sauce Labs Bike Light
|
|
13
|
+
standardPassword: '${DEMO_SHOP_PASSWORD:-secret_sauce}'
|
|
14
|
+
timeouts:
|
|
15
|
+
test: 90000
|
|
16
|
+
screenshots:
|
|
17
|
+
onlyOnFailure: false
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: staging
|
|
2
|
+
description: Public sandboxes (SauceDemo + JSONPlaceholder). No secrets except the demo password.
|
|
3
|
+
ui:
|
|
4
|
+
baseUrl: https://www.saucedemo.com
|
|
5
|
+
api:
|
|
6
|
+
baseUrl: https://jsonplaceholder.typicode.com
|
|
7
|
+
headers: { Accept: application/json }
|
|
8
|
+
auth: { type: none }
|
|
9
|
+
# For a real API:
|
|
10
|
+
# auth: { type: bearer, token: '${API_TOKEN}' }
|
|
11
|
+
# openapi: ./openapi/staging.json
|
|
12
|
+
users:
|
|
13
|
+
poolSize: 4
|
|
14
|
+
vars:
|
|
15
|
+
defaultProduct: Sauce Labs Backpack
|
|
16
|
+
standardPassword: '${DEMO_SHOP_PASSWORD:-secret_sauce}'
|
|
17
|
+
use:
|
|
18
|
+
locale: en-US
|
|
19
|
+
timezoneId: America/New_York
|
|
20
|
+
screenshots:
|
|
21
|
+
onlyOnFailure: false
|
|
22
|
+
perf:
|
|
23
|
+
budgets: { pageLoadMs: 5000 }
|
|
Binary file
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
@api @posts @har:posts
|
|
2
|
+
Feature: Posts API
|
|
3
|
+
JSONPlaceholder CRUD through the shared API step library: status codes, JSON-path assertions,
|
|
4
|
+
schema validation, variable chaining and response-time budgets.
|
|
5
|
+
|
|
6
|
+
@smoke
|
|
7
|
+
Scenario: List posts
|
|
8
|
+
When I send a GET request to "/posts"
|
|
9
|
+
Then the response status should be 200
|
|
10
|
+
And the response body should be an array
|
|
11
|
+
And the response JSON path "$.length" should equal "100"
|
|
12
|
+
And the response JSON path "[0].id" should equal "1"
|
|
13
|
+
And the response time should be under 3000 ms
|
|
14
|
+
|
|
15
|
+
@smoke @contract
|
|
16
|
+
Scenario: Get a single post and validate its shape
|
|
17
|
+
When I send a GET request to "/posts/1"
|
|
18
|
+
Then the response status should be 200
|
|
19
|
+
And the response JSON path "id" should equal "1"
|
|
20
|
+
And the response JSON path "userId" should equal "1"
|
|
21
|
+
And the response JSON path "title" should match ".+"
|
|
22
|
+
And the response should match the JSON schema "post"
|
|
23
|
+
And the response header "content-type" should contain "application/json"
|
|
24
|
+
|
|
25
|
+
@regression
|
|
26
|
+
Scenario: Nested resource - comments of a post
|
|
27
|
+
When I send a GET request to "/posts/1/comments"
|
|
28
|
+
Then the response status should be 200
|
|
29
|
+
And the response JSON path "$.length" should equal "5"
|
|
30
|
+
And the response JSON path "[0].postId" should equal "1"
|
|
31
|
+
And the response JSON path "[0].email" should match "@"
|
|
32
|
+
|
|
33
|
+
@regression
|
|
34
|
+
Scenario: Query parameters filter posts by user
|
|
35
|
+
Given I set the query parameter "userId" to "2"
|
|
36
|
+
When I send a GET request to "/posts"
|
|
37
|
+
Then the response status should be 200
|
|
38
|
+
And the response JSON path "$.length" should equal "10"
|
|
39
|
+
And the response JSON path "[0].userId" should equal "2"
|
|
40
|
+
|
|
41
|
+
@regression @contract
|
|
42
|
+
Scenario: Create a post and chain its id
|
|
43
|
+
Given I set the variable "title" to "SDODS created this"
|
|
44
|
+
When I send a POST request to "/posts" with body:
|
|
45
|
+
"""json
|
|
46
|
+
{ "title": "{{title}}", "body": "hello from SDODS", "userId": 1 }
|
|
47
|
+
"""
|
|
48
|
+
Then the response status should be 201
|
|
49
|
+
And the response JSON path "title" should equal "{{title}}"
|
|
50
|
+
And the response should match the JSON schema "post"
|
|
51
|
+
When I save the response JSON path "id" as "postId"
|
|
52
|
+
And I send a GET request to "/posts/{{postId}}"
|
|
53
|
+
Then the response status should be one of "200, 404"
|
|
54
|
+
|
|
55
|
+
@regression
|
|
56
|
+
Scenario: Update a post
|
|
57
|
+
When I send a PUT request to "/posts/1" with body:
|
|
58
|
+
"""json
|
|
59
|
+
{ "id": 1, "title": "updated by SDODS", "body": "changed", "userId": 1 }
|
|
60
|
+
"""
|
|
61
|
+
Then the response status should be 200
|
|
62
|
+
And the response JSON path "title" should equal "updated by SDODS"
|
|
63
|
+
When I send a PATCH request to "/posts/1" with body:
|
|
64
|
+
"""json
|
|
65
|
+
{ "title": "patched by SDODS" }
|
|
66
|
+
"""
|
|
67
|
+
Then the response status should be 200
|
|
68
|
+
And the response JSON path "title" should equal "patched by SDODS"
|
|
69
|
+
|
|
70
|
+
@regression
|
|
71
|
+
Scenario: Delete a post
|
|
72
|
+
When I send a DELETE request to "/posts/1"
|
|
73
|
+
Then the response status should be 200
|
|
74
|
+
|
|
75
|
+
@regression
|
|
76
|
+
Scenario: Unknown post returns 404
|
|
77
|
+
When I send a GET request to "/posts/999999"
|
|
78
|
+
Then the response status should be 404
|
|
79
|
+
|
|
80
|
+
@regression
|
|
81
|
+
Scenario: Data-driven request from the posts dataset
|
|
82
|
+
Given I load dataset "posts" row 0
|
|
83
|
+
When I send a GET request to "/posts/{{id}}"
|
|
84
|
+
Then the response status should be 200
|
|
85
|
+
And the response JSON path "userId" should equal "{{userId}}"
|
|
86
|
+
|
|
87
|
+
@regression
|
|
88
|
+
Scenario: Polling until a condition holds
|
|
89
|
+
When I poll GET "/posts/2" until JSON path "id" equals "2" within 10 seconds
|
|
90
|
+
Then the response status should be 200
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@ui @auth @data-driven
|
|
2
|
+
Feature: Data-driven login
|
|
3
|
+
Credentials come from the users dataset (data/<env>/users.csv with data/common fallback).
|
|
4
|
+
|
|
5
|
+
@regression
|
|
6
|
+
Scenario: Login with the first user of the dataset
|
|
7
|
+
Given I load dataset "users" row 0
|
|
8
|
+
And I am on the login page
|
|
9
|
+
When I login with "{{username}}" and "{{password}}"
|
|
10
|
+
Then I should be on the inventory page
|
|
11
|
+
|
|
12
|
+
@regression
|
|
13
|
+
Scenario: Login with the user whose role is problem
|
|
14
|
+
Given I load dataset "users" where "role" is "problem"
|
|
15
|
+
And I am on the login page
|
|
16
|
+
When I login with "{{username}}" and "{{password}}"
|
|
17
|
+
Then I should be on the inventory page
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
@ui @auth
|
|
2
|
+
Feature: Login
|
|
3
|
+
As a shopper I want to sign in so that I can see the inventory.
|
|
4
|
+
|
|
5
|
+
Background:
|
|
6
|
+
Given I am on the login page
|
|
7
|
+
|
|
8
|
+
@smoke @har:login
|
|
9
|
+
Scenario: Successful login shows the products
|
|
10
|
+
When I login with "standard_user" and "{{standardPassword}}"
|
|
11
|
+
Then I should be on the inventory page
|
|
12
|
+
And the inventory title should be "Products"
|
|
13
|
+
|
|
14
|
+
@regression
|
|
15
|
+
Scenario Outline: Failed login shows a clear error
|
|
16
|
+
When I login with "<username>" and "<password>"
|
|
17
|
+
Then I should see the login error "<error>"
|
|
18
|
+
|
|
19
|
+
# title-format: <username> → <error>
|
|
20
|
+
Examples:
|
|
21
|
+
| username | password | error |
|
|
22
|
+
| locked_out_user | secret_sauce | Epic sadface: Sorry, this user has been locked out. |
|
|
23
|
+
| standard_user | wrong | Epic sadface: Username and password do not match any user in this service |
|
|
24
|
+
| | secret_sauce | Epic sadface: Username is required |
|
|
25
|
+
| standard_user | | Epic sadface: Password is required |
|
|
26
|
+
|
|
27
|
+
@regression
|
|
28
|
+
Scenario: Generic UI steps work against the login form
|
|
29
|
+
When I fill the element with test id "username" with "standard_user"
|
|
30
|
+
And I fill the element with test id "password" with "{{standardPassword}}"
|
|
31
|
+
And I click the element with test id "login-button"
|
|
32
|
+
Then the page URL should contain "/inventory.html"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
@ui @auth @pool
|
|
2
|
+
Feature: User pool
|
|
3
|
+
Accounts are leased per worker from the pool dataset; login state is cached as storageState.
|
|
4
|
+
|
|
5
|
+
@sanity @user:standard
|
|
6
|
+
Scenario: A leased standard user starts logged in through cached storage state
|
|
7
|
+
Given I navigate to the "inventory" page
|
|
8
|
+
Then I should be on the inventory page
|
|
9
|
+
And the inventory title should be "Products"
|
|
10
|
+
|
|
11
|
+
@sanity
|
|
12
|
+
Scenario: Leasing a user by role inside the scenario
|
|
13
|
+
Given I use a leased user with role "problem"
|
|
14
|
+
And I navigate to the "inventory" page
|
|
15
|
+
Then I should be on the inventory page
|
|
16
|
+
|
|
17
|
+
@sanity @user:performance
|
|
18
|
+
Scenario: A second role leases a different account
|
|
19
|
+
Given I navigate to the "inventory" page
|
|
20
|
+
Then I should be on the inventory page
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@ui @cart
|
|
2
|
+
Feature: Cart
|
|
3
|
+
Adding products and reviewing them in the cart.
|
|
4
|
+
|
|
5
|
+
Background:
|
|
6
|
+
Given I am on the login page
|
|
7
|
+
And I login with "standard_user" and "{{standardPassword}}"
|
|
8
|
+
|
|
9
|
+
@smoke @har:cart
|
|
10
|
+
Scenario: A product added on the inventory page appears in the cart
|
|
11
|
+
When I add "Sauce Labs Backpack" to the cart
|
|
12
|
+
And I open the cart
|
|
13
|
+
Then the cart should list "Sauce Labs Backpack"
|
|
14
|
+
|
|
15
|
+
@regression
|
|
16
|
+
Scenario: Continue shopping returns to the inventory
|
|
17
|
+
When I open the cart
|
|
18
|
+
And I click the "Continue Shopping" button
|
|
19
|
+
Then I should be on the inventory page
|
|
20
|
+
|
|
21
|
+
@regression
|
|
22
|
+
Scenario: Checkout form is reachable from the cart
|
|
23
|
+
When I add "Sauce Labs Bolt T-Shirt" to the cart
|
|
24
|
+
And I open the cart
|
|
25
|
+
And I click the "Checkout" button
|
|
26
|
+
Then the page URL should contain "/checkout-step-one.html"
|
|
27
|
+
When I fill the form:
|
|
28
|
+
| field | value |
|
|
29
|
+
| First Name | Auto |
|
|
30
|
+
| Last Name | Max |
|
|
31
|
+
| Zip/Postal Code | 12345 |
|
|
32
|
+
And I click the "Continue" button
|
|
33
|
+
Then the page URL should contain "/checkout-step-two.html"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
@hybrid @hybrid-demo
|
|
2
|
+
Feature: Seed through the API, verify in the browser
|
|
3
|
+
One scenario, both layers: the API client and the page share the same fixtures and variables.
|
|
4
|
+
|
|
5
|
+
@regression
|
|
6
|
+
Scenario: A post created through the API is rendered by the UI
|
|
7
|
+
Given I use a leased user with role "standard"
|
|
8
|
+
When I seed via POST "/posts" with body:
|
|
9
|
+
"""json
|
|
10
|
+
{ "title": "SDODS hybrid {{username}}", "body": "seeded", "userId": 1 }
|
|
11
|
+
"""
|
|
12
|
+
Then the response status should be 201
|
|
13
|
+
When I save the response JSON path "title" as "title"
|
|
14
|
+
And I mock "**/inventory.html" with HTML:
|
|
15
|
+
"""
|
|
16
|
+
<main><h1>{{title}}</h1><p>rendered from the seeded API response</p></main>
|
|
17
|
+
"""
|
|
18
|
+
And I navigate to the "inventory" page
|
|
19
|
+
Then the UI should show the text from JSON path "title"
|
|
20
|
+
|
|
21
|
+
@regression
|
|
22
|
+
Scenario: API state and UI state in one flow
|
|
23
|
+
When I send a GET request to "/users/1"
|
|
24
|
+
Then the response status should be 200
|
|
25
|
+
When I save the response JSON path "username" as "apiUser"
|
|
26
|
+
Given I am on the login page
|
|
27
|
+
When I fill the element with test id "username" with "{{apiUser}}"
|
|
28
|
+
Then the element with test id "username" should be visible
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
@ui @inventory
|
|
2
|
+
Feature: Inventory
|
|
3
|
+
Products page: listing, cart badge and sorting.
|
|
4
|
+
|
|
5
|
+
Background:
|
|
6
|
+
Given I am on the login page
|
|
7
|
+
And I login with "standard_user" and "{{standardPassword}}"
|
|
8
|
+
|
|
9
|
+
@smoke @har:products
|
|
10
|
+
Scenario: Products are displayed
|
|
11
|
+
Then the inventory title should be "Products"
|
|
12
|
+
And there should be 6 products listed
|
|
13
|
+
|
|
14
|
+
@regression
|
|
15
|
+
Scenario: Add a product to the cart
|
|
16
|
+
When I add "{{defaultProduct}}" to the cart
|
|
17
|
+
Then the cart badge should show 1 item
|
|
18
|
+
|
|
19
|
+
@regression
|
|
20
|
+
Scenario: Remove a product from the cart
|
|
21
|
+
When I add "Sauce Labs Bike Light" to the cart
|
|
22
|
+
And I remove "Sauce Labs Bike Light" from the cart
|
|
23
|
+
Then the cart badge should be hidden
|
|
24
|
+
|
|
25
|
+
@regression
|
|
26
|
+
Scenario: Sort products by price ascending
|
|
27
|
+
When I sort products by "Price (low to high)"
|
|
28
|
+
Then the product prices should be sorted ascending
|
|
29
|
+
|
|
30
|
+
@regression
|
|
31
|
+
Scenario: Sort products by name descending
|
|
32
|
+
When I sort products by "Name (Z to A)"
|
|
33
|
+
Then the product names should be sorted descending
|
|
34
|
+
|
|
35
|
+
@regression @skip:webkit
|
|
36
|
+
Scenario: Open the menu (browser-specific exclusion example)
|
|
37
|
+
When I click the "Open Menu" button
|
|
38
|
+
Then the "Logout" link should be visible
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@ui @inventory @mock
|
|
2
|
+
Feature: Network interception
|
|
3
|
+
Route mocking with the shared steps, and a direct API check from a UI scenario.
|
|
4
|
+
|
|
5
|
+
@regression
|
|
6
|
+
Scenario: A mocked page is rendered instead of the real one
|
|
7
|
+
Given I mock "**/mocked/**" with HTML:
|
|
8
|
+
"""
|
|
9
|
+
<h1 data-test="banner">Mocked by SDODS</h1>
|
|
10
|
+
"""
|
|
11
|
+
When I navigate to the "/mocked/banner.html" page
|
|
12
|
+
Then I should see the text "Mocked by SDODS"
|
|
13
|
+
And the element with test id "banner" should be visible
|
|
14
|
+
|
|
15
|
+
@regression
|
|
16
|
+
Scenario: A mocked JSON API response is observed through the API client
|
|
17
|
+
Given I am on the login page
|
|
18
|
+
And I mock "**/api/health" with JSON:
|
|
19
|
+
"""json
|
|
20
|
+
{ "status": "mocked" }
|
|
21
|
+
"""
|
|
22
|
+
When I send a GET request to "https://www.saucedemo.com/"
|
|
23
|
+
Then the response status should be 200
|
|
24
|
+
|
|
25
|
+
@smoke @har:site
|
|
26
|
+
Scenario: The site answers over HTTP
|
|
27
|
+
When I send a GET request to "https://www.saucedemo.com/"
|
|
28
|
+
Then the response status should be 200
|
|
29
|
+
And the response time should be under 5000 ms
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
@ui @inventory @visual
|
|
2
|
+
Feature: Inventory visual baseline
|
|
3
|
+
Pixel comparison against a per-browser baseline (run once with --update-snapshots).
|
|
4
|
+
|
|
5
|
+
@regression
|
|
6
|
+
Scenario: Inventory page matches its baseline
|
|
7
|
+
Given I am on the login page
|
|
8
|
+
And I login with "standard_user" and "{{standardPassword}}"
|
|
9
|
+
Then I should be on the inventory page
|
|
10
|
+
And the page should match the visual baseline "inventory"
|