Skip to content

Commit 160c5be

Browse files
authored
Merge pull request #914 from w3bdesign/dev
Add test for products
2 parents 71356cc + 55a4fc0 commit 160c5be

File tree

7 files changed

+237
-14
lines changed

7 files changed

+237
-14
lines changed

.circleci/config.yml

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ orbs:
33
browser-tools: circleci/browser-tools@1.4.1
44

55
jobs: # a collection of steps
6-
build: # runs not using Workflows must have a `build` job as entry point
6+
dusk: # runs not using Workflows must have a `build` job as entry point
77
docker: # run the steps with Docker
8-
#- image: cimg/php:8.0.14-browsers # ...with this image as the primary container; this is where all `steps` will run
98
- image: cimg/php:8.2.4-browsers # ...with this image as the primary container; this is where all `steps` will run
109
auth:
1110
username: mydockerhub-user
12-
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
11+
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
1312
working_directory: ~/laravel # directory where steps will run
1413
steps: # a set of executable commands
1514
- checkout # special step to check out source code to working directory
@@ -45,14 +44,38 @@ jobs: # a collection of steps
4544
- run:
4645
name: Serve Application
4746
background: true
48-
command: php artisan serve
47+
command: php artisan serve
4948
- run: php artisan dusk
50-
- run: php artisan test
49+
- run: php artisan test
5150
- store_artifacts:
5251
path: ./tests/Browser/console
5352
destination: console
5453
- store_artifacts:
5554
path: ./tests/Browser/screenshots
5655
destination: screenshots
5756
# See https://circleci.com/docs/2.0/deployment-integrations/ for deploy examples
58-
resource_class: large
57+
resource_class: large
58+
59+
vue-tests:
60+
docker:
61+
- image: cimg/node:16.5.0
62+
working_directory: ~/laravel
63+
steps:
64+
- checkout
65+
- restore_cache:
66+
keys:
67+
- node-v1-{{ checksum "package-lock.json" }}
68+
- node-v1-
69+
- run: npm ci
70+
- save_cache:
71+
key: node-v1-{{ checksum "package-lock.json" }}
72+
paths:
73+
- node_modules
74+
- run: npm run test # Assuming you have a "test" script in your package.json for running Vue tests
75+
76+
workflows:
77+
version: 2
78+
build-and-test:
79+
jobs:
80+
- dusk
81+
- vue-tests

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Ecommerce site with Laravel 10, Vue 3 and Stripe.
4242

4343
- SonarCloud code quality scanner integration on all pull requests
4444

45-
- Laravel tests with CircleCI integration
45+
- Laravel Dusk and Jest tests with CircleCI integration
4646

4747
## Main dependencies:
4848

@@ -101,8 +101,6 @@ Ecommerce site with Laravel 10, Vue 3 and Stripe.
101101

102102
- Do WCAG analysis and ensure there are no issues
103103

104-
- Add some tests to verify that the cart and checkout works correctly
105-
106104
- Consider adding an admin dashboard
107105

108106
- Look into performance optimization

tests/Browser/ExampleTest.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ public function testBasicExample()
1616
{
1717
$this->browse(function (Browser $browser) {
1818
$browser->visit('/')
19-
//->screenshot('home-page')
20-
->assertSee('MacBook');
21-
//->assertPathIs('/');
19+
->assertSee('MacBook')
20+
->assertPathIs('/');
2221
});
2322
}
2423
}

tests/Vue/Button/button.spec.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import BaseButton from "../../../resources/js/components/base/BaseButton.vue";
44
import "@testing-library/jest-dom";
55

66
test("Verify that the button renders and that the default slot works", async () => {
7-
// The render method returns a collection of utilities to query your component.
87
const { getByText } = render(BaseButton, {
98
slots: { default: "Button test" },
109
});
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
1-
// https://github.com/testing-library/vue-testing-library/tree/main/src/__tests__
1+
import { render } from "@testing-library/vue";
2+
import BaseLoadingSpinner from "../../../resources/js/components/base/BaseLoadingSpinner.vue";
3+
4+
import "@testing-library/jest-dom";
5+
6+
test("Verify that the loading message is rendered", async () => {
7+
const { getByText } = render(BaseLoadingSpinner);
8+
9+
const loadingMessage = getByText("Loading products ...");
10+
expect(loadingMessage).toBeInTheDocument();
11+
});
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { render } from "@testing-library/vue";
2+
import ShowAllProducts from "../../../resources/js/components/Products/ShowAllProducts.vue";
3+
import formatPrice from "../../../resources/js/utils/functions";
4+
5+
import "@testing-library/jest-dom";
6+
7+
jest.mock("swrv", () => ({
8+
__esModule: true,
9+
default: jest.fn(),
10+
}));
11+
12+
const useRouterLink = {
13+
template: "<a><slot></slot></a>",
14+
};
15+
16+
/**
17+
* @description
18+
* Test case to verify that products are rendered correctly with their respective details,
19+
* and that the router-link component works as expected.
20+
*
21+
* The test will:
22+
* 1. Mock the `useSWRV` hook to return a list of products.
23+
* 2. Render the `ShowAllProducts` component.
24+
* 3. Iterate through each product and check if the product's name, price, and image are present in the DOM.
25+
* 4. Check if the list of images found by the role "img" matches the length of the products list.
26+
*
27+
* @async
28+
* @function
29+
* @name verifyProductsAreRenderedAndRouterLinkWorksTest
30+
*/
31+
test("Verify products are rendered and router-link works", async () => {
32+
const useSWRV = require("swrv").default;
33+
const products = [
34+
{
35+
id: 1,
36+
name: "Product 1",
37+
price: 100,
38+
slug: "product-1",
39+
imageUrl: "image1.jpg",
40+
},
41+
{
42+
id: 2,
43+
name: "Product 2",
44+
price: 200,
45+
slug: "product-2",
46+
imageUrl: "image2.jpg",
47+
},
48+
];
49+
useSWRV.mockReturnValue({ data: products, error: null });
50+
51+
const { getByText, getAllByRole } = render(ShowAllProducts, {
52+
global: {
53+
components: {
54+
"router-link": useRouterLink,
55+
},
56+
},
57+
});
58+
59+
for (const product of products) {
60+
expect(getByText(product.name)).toBeInTheDocument();
61+
expect(getByText(formatPrice(product.price))).toBeInTheDocument();
62+
63+
const productLink = getByText(product.name).closest("a");
64+
const productImage = productLink.querySelector("img");
65+
expect(productImage).toHaveAttribute("src", product.imageUrl);
66+
}
67+
68+
const images = getAllByRole("img");
69+
expect(images.length).toBe(products.length);
70+
});
71+
72+
/**
73+
* @description
74+
* Test case to verify that an error message is displayed when an error occurs while loading products.
75+
*
76+
* The test will:
77+
* 1. Mock the `useSWRV` hook to return an error.
78+
* 2. Render the `ShowAllProducts` component.
79+
* 3. Check if the error message is present in the DOM.
80+
*
81+
* @async
82+
* @function
83+
* @name verifyErrorMessageIsDisplayedWhenErrorOccursTest
84+
*/
85+
test("Verify error message is displayed when error occurs", async () => {
86+
const useSWRV = require("swrv").default;
87+
useSWRV.mockReturnValue({
88+
data: null,
89+
error: new Error("Error loading products"),
90+
});
91+
92+
const { getByText } = render(ShowAllProducts, {
93+
global: {
94+
components: {
95+
"router-link": useRouterLink,
96+
},
97+
},
98+
});
99+
100+
const errorMessage = getByText("Error loading products");
101+
expect(errorMessage).toBeInTheDocument();
102+
});
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { fireEvent, render } from "@testing-library/vue";
2+
3+
import SingleProduct from "../../../resources/js/components/Products/SingleProduct.vue";
4+
import { useCart } from "../../../resources/js/store/useCart";
5+
import formatPrice from "../../../resources/js/utils/functions";
6+
7+
import "@testing-library/jest-dom";
8+
9+
jest.mock("swrv", () => ({
10+
__esModule: true,
11+
default: jest.fn(),
12+
}));
13+
14+
// Mock the useCart function
15+
jest.mock("@/store/useCart", () => {
16+
const addToCartMock = jest.fn();
17+
return {
18+
useCart: jest.fn(() => ({ addToCart: addToCartMock })),
19+
__addToCartMock: addToCartMock,
20+
};
21+
});
22+
23+
// Mock the useRoute function
24+
jest.mock("vue-router", () => ({
25+
useRoute: jest.fn(() => ({
26+
params: {
27+
slug: "product-1",
28+
},
29+
})),
30+
}));
31+
32+
const useRouterLink = {
33+
template: "<a><slot></slot></a>",
34+
};
35+
36+
/**
37+
* Test case to verify the 'Add to Cart' functionality.
38+
*
39+
* @name Verify 'Add to Cart' functionality
40+
* @function
41+
* @async
42+
* @memberof module:tests
43+
*
44+
* @description
45+
* The test case does the following:
46+
* 1. Mocks the `useSWRV` hook to return the product data.
47+
* 2. Mocks the `useCart` store to use a Jest function as the `addToCart` method.
48+
* 3. Renders the `SingleProduct` component with the necessary global components.
49+
* 4. Verifies the product information is displayed.
50+
* 5. Clicks the 'Add To Cart' button.
51+
* 6. Verifies that the `addToCart` method in the store is called with the correct product.
52+
*
53+
* @returns {void}
54+
*/
55+
test("Verify 'Add to Cart' functionality", async () => {
56+
const useSWRV = require("swrv").default;
57+
const product = {
58+
id: 1,
59+
name: "Product 1",
60+
price: 100,
61+
slug: "product-1",
62+
imageUrl: "image1.jpg",
63+
description: "Sample description",
64+
};
65+
useSWRV.mockReturnValue({ data: product, error: null });
66+
67+
const addToCartMock = jest.fn();
68+
useCart.mockReturnValue({
69+
addToCart: addToCartMock,
70+
});
71+
72+
const { getByText } = render(SingleProduct, {
73+
global: {
74+
components: {
75+
"router-link": useRouterLink,
76+
},
77+
},
78+
});
79+
80+
// Verify product information is displayed
81+
expect(getByText(product.name)).toBeInTheDocument();
82+
expect(getByText(formatPrice(product.price))).toBeInTheDocument();
83+
expect(getByText(product.description)).toBeInTheDocument();
84+
85+
// Click the 'Add To Cart' button
86+
const addToCartButton = getByText("Add To Cart");
87+
await fireEvent.click(addToCartButton);
88+
89+
// Verify that the `addToCart` method in the store is called with the correct product
90+
expect(addToCartMock).toHaveBeenCalledTimes(1);
91+
expect(addToCartMock).toHaveBeenCalledWith({ item: product });
92+
});

0 commit comments

Comments
 (0)