Skip to content

Commit 6319c25

Browse files
committed
add examples for visual regression testing
1 parent 11dba5c commit 6319c25

File tree

18 files changed

+6085
-0
lines changed

18 files changed

+6085
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"nodeVersion": "system",
3+
"experimentalFetchPolyfill": true
4+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const environmentName = process.env.NODE_ENV;
2+
const db = require("knex")(require("./knexfile")[environmentName]);
3+
4+
const closeConnection = () => db.destroy();
5+
6+
module.exports = {
7+
db,
8+
closeConnection
9+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "Using fixtures to represent data",
3+
"email": "hello@cypress.io",
4+
"body": "Fixtures are a great way to mock data for responses to routes"
5+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { InventoryManagement } from "../pageObjects/inventoryManagement";
2+
3+
const now = new Date(1996, 5, 2).getTime();
4+
5+
describe("item list", () => {
6+
beforeEach(() => cy.task("emptyInventory"));
7+
8+
it("can update an item's quantity", () => {
9+
cy.task("seedItem", { itemName: "cheesecake", quantity: 1 });
10+
InventoryManagement.visit();
11+
InventoryManagement.findItemEntry("cheesecake", "1");
12+
cy.percySnapshot();
13+
});
14+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { InventoryManagement } from "../pageObjects/inventoryManagement";
2+
3+
describe("item list updates", () => {
4+
beforeEach(() => cy.task("emptyInventory"));
5+
6+
describe("when the application loads for the first time", () => {
7+
it.only("loads the initial list of items", () => {
8+
cy.addItem("cheesecake", 2);
9+
cy.addItem("apple pie", 5);
10+
cy.addItem("carrot cake", 96);
11+
cy.visit("http://localhost:8080");
12+
cy.wait(1);
13+
14+
InventoryManagement.findItemEntry("cheesecake", "2");
15+
InventoryManagement.findItemEntry("apple pie", "5");
16+
InventoryManagement.findItemEntry("carrot cake", "96");
17+
});
18+
});
19+
20+
describe("as other users add items", () => {
21+
it("updates the item list", () => {
22+
cy.server()
23+
.route("http://localhost:3000/inventory")
24+
.as("inventoryRequest");
25+
cy.visit("http://localhost:8080");
26+
cy.wait("@inventoryRequest");
27+
cy.addItem("cheesecake", 22);
28+
InventoryManagement.findItemEntry("cheesecake", "22");
29+
});
30+
});
31+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { InventoryManagement } from "../pageObjects/inventoryManagement";
2+
3+
describe("item submission", () => {
4+
beforeEach(() => cy.task("emptyInventory"));
5+
beforeEach(() => {
6+
cy.server();
7+
cy.route("GET", "/inventory/cheesecake", {
8+
recipes: [
9+
{ href: "http://example.com/always-the-same-url/first-recipe" },
10+
{ href: "http://example.com/always-the-same-url/second-recipe" },
11+
{ href: "http://example.com/always-the-same-url/third-recipe" }
12+
]
13+
});
14+
});
15+
16+
it("can add items through the form", () => {
17+
InventoryManagement.visit();
18+
cy.window().then(w => cy.stub(w.Math, "random").returns(0.5));
19+
InventoryManagement.addItem("cheesecake", "10");
20+
InventoryManagement.findItemEntry("cheesecake", "10")
21+
.get("a")
22+
.should(
23+
"have.attr",
24+
"href",
25+
"http://example.com/always-the-same-url/second-recipe"
26+
);
27+
});
28+
29+
it("can update an item's quantity", () => {
30+
cy.task("seedItem", { itemName: "cheesecake", quantity: 5 });
31+
InventoryManagement.visit();
32+
InventoryManagement.addItem("cheesecake", "10");
33+
InventoryManagement.findItemEntry("cheesecake", "15");
34+
});
35+
36+
it("can undo submitted items", () => {
37+
InventoryManagement.visit();
38+
InventoryManagement.findAction({});
39+
40+
cy.window().then(({ handleAddItem }) => handleAddItem("cheesecake", 10));
41+
InventoryManagement.findAction({ cheesecake: 10 });
42+
43+
cy.window().then(({ handleAddItem }) => handleAddItem("cheesecake", 5));
44+
InventoryManagement.findAction({ cheesecake: 15 });
45+
46+
InventoryManagement.undo();
47+
InventoryManagement.findItemEntry("cheesecake", "10");
48+
});
49+
50+
it.only("saves each submission to the action log", () => {
51+
cy.clock();
52+
InventoryManagement.visit();
53+
InventoryManagement.findAction({});
54+
cy.clock().tick(2000);
55+
56+
InventoryManagement.addItem("cheesecake", "10");
57+
InventoryManagement.findAction({ cheesecake: 10 });
58+
cy.clock().tick(2000);
59+
60+
InventoryManagement.addItem("cheesecake", "5");
61+
InventoryManagement.findAction({ cheesecake: 15 });
62+
cy.clock().tick(2000);
63+
64+
InventoryManagement.undo();
65+
66+
InventoryManagement.findItemEntry("cheesecake", "10");
67+
InventoryManagement.findAction({ cheesecake: 10 });
68+
});
69+
70+
describe("given a user enters an invalid item name", () => {
71+
it("disables the form's submission button", () => {
72+
InventoryManagement.visit();
73+
InventoryManagement.enterItemName("boat");
74+
InventoryManagement.enterQuantity(10);
75+
InventoryManagement.getSubmitButton().should("be.disabled");
76+
});
77+
});
78+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
development: {
3+
client: "sqlite3",
4+
connection: { filename: "../server/dev.sqlite" },
5+
useNullAsDefault: true
6+
}
7+
};
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
export class InventoryManagement {
2+
static visit() {
3+
cy.visit("http://localhost:8080");
4+
}
5+
6+
static enterItemName(itemName) {
7+
return cy
8+
.get('input[placeholder="Item name"]')
9+
.clear()
10+
.type(itemName);
11+
}
12+
13+
static enterQuantity(quantity) {
14+
return cy
15+
.get('input[placeholder="Quantity"]')
16+
.clear()
17+
.type(quantity);
18+
}
19+
20+
static getSubmitButton() {
21+
return cy.get('button[type="submit"]').contains("Add to inventory");
22+
}
23+
24+
static addItem(itemName, quantity) {
25+
InventoryManagement.enterItemName(itemName);
26+
InventoryManagement.enterQuantity(quantity);
27+
InventoryManagement.getSubmitButton().click();
28+
}
29+
30+
static findItemEntry(itemName, quantity) {
31+
return cy.contains("li", `${itemName} - Quantity: ${quantity}`);
32+
}
33+
34+
static undo() {
35+
return cy
36+
.get("button")
37+
.contains("Undo")
38+
.click();
39+
}
40+
41+
static findAction(inventoryState) {
42+
return cy.clock(c => {
43+
const dateText = new Date(c.details().now).toISOString();
44+
return cy
45+
.get("p:not(:nth-of-type(1))")
46+
.contains(
47+
`[${dateText}]` +
48+
" The inventory has been updated - " +
49+
JSON.stringify(inventoryState)
50+
);
51+
});
52+
}
53+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const { db } = require("../dbConnection");
2+
3+
const dbPlugin = (on, config) => {
4+
on(
5+
"task",
6+
{
7+
emptyInventory: () => db("inventory").truncate(),
8+
seedItem: itemRow => db("inventory").insert(itemRow)
9+
},
10+
config
11+
);
12+
13+
return config;
14+
};
15+
16+
module.exports = dbPlugin;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// <reference types="cypress" />
2+
// ***********************************************************
3+
// This example plugins/index.js can be used to load plugins
4+
//
5+
// You can change the location of this file or turn off loading
6+
// the plugins file with the 'pluginsFile' configuration option.
7+
//
8+
// You can read more here:
9+
// https://on.cypress.io/plugins-guide
10+
// ***********************************************************
11+
12+
// This function is called when a project is opened or re-opened (e.g. due to
13+
// the project's config changing)
14+
15+
const percyHealthCheck = require("@percy/cypress/task");
16+
const dbPlugin = require("./dbPlugin");
17+
18+
module.exports = (on, config) => {
19+
dbPlugin(on, config);
20+
on("task", percyHealthCheck);
21+
};

0 commit comments

Comments
 (0)