Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ orbs:
browser-tools: circleci/[email protected]

jobs: # a collection of steps
build: # runs not using Workflows must have a `build` job as entry point
dusk: # runs not using Workflows must have a `build` job as entry point
docker: # run the steps with Docker
#- image: cimg/php:8.0.14-browsers # ...with this image as the primary container; this is where all `steps` will run
- image: cimg/php:8.2.4-browsers # ...with this image as the primary container; this is where all `steps` will run
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
working_directory: ~/laravel # directory where steps will run
steps: # a set of executable commands
- checkout # special step to check out source code to working directory
Expand Down Expand Up @@ -45,14 +44,38 @@ jobs: # a collection of steps
- run:
name: Serve Application
background: true
command: php artisan serve
command: php artisan serve
- run: php artisan dusk
- run: php artisan test
- run: php artisan test
- store_artifacts:
path: ./tests/Browser/console
destination: console
- store_artifacts:
path: ./tests/Browser/screenshots
destination: screenshots
# See https://circleci.com/docs/2.0/deployment-integrations/ for deploy examples
resource_class: large
resource_class: large

vue-tests:
docker:
- image: cimg/node:16.5.0
working_directory: ~/laravel
steps:
- checkout
- restore_cache:
keys:
- node-v1-{{ checksum "package-lock.json" }}
- node-v1-
- run: npm ci
- save_cache:
key: node-v1-{{ checksum "package-lock.json" }}
paths:
- node_modules
- run: npm run test # Assuming you have a "test" script in your package.json for running Vue tests

workflows:
version: 2
build-and-test:
jobs:
- dusk
- vue-tests
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Ecommerce site with Laravel 10, Vue 3 and Stripe.

- SonarCloud code quality scanner integration on all pull requests

- Laravel tests with CircleCI integration
- Laravel Dusk and Jest tests with CircleCI integration

## Main dependencies:

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

- Do WCAG analysis and ensure there are no issues

- Add some tests to verify that the cart and checkout works correctly

- Consider adding an admin dashboard

- Look into performance optimization
5 changes: 2 additions & 3 deletions tests/Browser/ExampleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
//->screenshot('home-page')
->assertSee('MacBook');
//->assertPathIs('/');
->assertSee('MacBook')
->assertPathIs('/');
});
}
}
1 change: 0 additions & 1 deletion tests/Vue/Button/button.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import BaseButton from "../../../resources/js/components/base/BaseButton.vue";
import "@testing-library/jest-dom";

test("Verify that the button renders and that the default slot works", async () => {
// The render method returns a collection of utilities to query your component.
const { getByText } = render(BaseButton, {
slots: { default: "Button test" },
});
Expand Down
12 changes: 11 additions & 1 deletion tests/Vue/LoadingSpinner/loadingspinner.spec.js
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
// https://github.com/testing-library/vue-testing-library/tree/main/src/__tests__
import { render } from "@testing-library/vue";
import BaseLoadingSpinner from "../../../resources/js/components/base/BaseLoadingSpinner.vue";

import "@testing-library/jest-dom";

test("Verify that the loading message is rendered", async () => {
const { getByText } = render(BaseLoadingSpinner);

const loadingMessage = getByText("Loading products ...");
expect(loadingMessage).toBeInTheDocument();
});
102 changes: 102 additions & 0 deletions tests/Vue/Products/ShowallProducts.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { render } from "@testing-library/vue";
import ShowAllProducts from "../../../resources/js/components/Products/ShowAllProducts.vue";
import formatPrice from "../../../resources/js/utils/functions";

import "@testing-library/jest-dom";

jest.mock("swrv", () => ({
__esModule: true,
default: jest.fn(),
}));

const useRouterLink = {
template: "<a><slot></slot></a>",
};

/**
* @description
* Test case to verify that products are rendered correctly with their respective details,
* and that the router-link component works as expected.
*
* The test will:
* 1. Mock the `useSWRV` hook to return a list of products.
* 2. Render the `ShowAllProducts` component.
* 3. Iterate through each product and check if the product's name, price, and image are present in the DOM.
* 4. Check if the list of images found by the role "img" matches the length of the products list.
*
* @async
* @function
* @name verifyProductsAreRenderedAndRouterLinkWorksTest
*/
test("Verify products are rendered and router-link works", async () => {
const useSWRV = require("swrv").default;
const products = [
{
id: 1,
name: "Product 1",
price: 100,
slug: "product-1",
imageUrl: "image1.jpg",
},
{
id: 2,
name: "Product 2",
price: 200,
slug: "product-2",
imageUrl: "image2.jpg",
},
];
useSWRV.mockReturnValue({ data: products, error: null });

const { getByText, getAllByRole } = render(ShowAllProducts, {
global: {
components: {
"router-link": useRouterLink,
},
},
});

for (const product of products) {
expect(getByText(product.name)).toBeInTheDocument();
expect(getByText(formatPrice(product.price))).toBeInTheDocument();

const productLink = getByText(product.name).closest("a");
const productImage = productLink.querySelector("img");
expect(productImage).toHaveAttribute("src", product.imageUrl);
}

const images = getAllByRole("img");
expect(images.length).toBe(products.length);
});

/**
* @description
* Test case to verify that an error message is displayed when an error occurs while loading products.
*
* The test will:
* 1. Mock the `useSWRV` hook to return an error.
* 2. Render the `ShowAllProducts` component.
* 3. Check if the error message is present in the DOM.
*
* @async
* @function
* @name verifyErrorMessageIsDisplayedWhenErrorOccursTest
*/
test("Verify error message is displayed when error occurs", async () => {
const useSWRV = require("swrv").default;
useSWRV.mockReturnValue({
data: null,
error: new Error("Error loading products"),
});

const { getByText } = render(ShowAllProducts, {
global: {
components: {
"router-link": useRouterLink,
},
},
});

const errorMessage = getByText("Error loading products");
expect(errorMessage).toBeInTheDocument();
});
92 changes: 92 additions & 0 deletions tests/Vue/Products/SingleProduct.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { fireEvent, render } from "@testing-library/vue";

import SingleProduct from "../../../resources/js/components/Products/SingleProduct.vue";
import { useCart } from "../../../resources/js/store/useCart";
import formatPrice from "../../../resources/js/utils/functions";

import "@testing-library/jest-dom";

jest.mock("swrv", () => ({
__esModule: true,
default: jest.fn(),
}));

// Mock the useCart function
jest.mock("@/store/useCart", () => {
const addToCartMock = jest.fn();
return {
useCart: jest.fn(() => ({ addToCart: addToCartMock })),
__addToCartMock: addToCartMock,
};
});

// Mock the useRoute function
jest.mock("vue-router", () => ({
useRoute: jest.fn(() => ({
params: {
slug: "product-1",
},
})),
}));

const useRouterLink = {
template: "<a><slot></slot></a>",
};

/**
* Test case to verify the 'Add to Cart' functionality.
*
* @name Verify 'Add to Cart' functionality
* @function
* @async
* @memberof module:tests
*
* @description
* The test case does the following:
* 1. Mocks the `useSWRV` hook to return the product data.
* 2. Mocks the `useCart` store to use a Jest function as the `addToCart` method.
* 3. Renders the `SingleProduct` component with the necessary global components.
* 4. Verifies the product information is displayed.
* 5. Clicks the 'Add To Cart' button.
* 6. Verifies that the `addToCart` method in the store is called with the correct product.
*
* @returns {void}
*/
test("Verify 'Add to Cart' functionality", async () => {
const useSWRV = require("swrv").default;
const product = {
id: 1,
name: "Product 1",
price: 100,
slug: "product-1",
imageUrl: "image1.jpg",
description: "Sample description",
};
useSWRV.mockReturnValue({ data: product, error: null });

const addToCartMock = jest.fn();
useCart.mockReturnValue({
addToCart: addToCartMock,
});

const { getByText } = render(SingleProduct, {
global: {
components: {
"router-link": useRouterLink,
},
},
});

// Verify product information is displayed
expect(getByText(product.name)).toBeInTheDocument();
expect(getByText(formatPrice(product.price))).toBeInTheDocument();
expect(getByText(product.description)).toBeInTheDocument();

// Click the 'Add To Cart' button
const addToCartButton = getByText("Add To Cart");
await fireEvent.click(addToCartButton);

// Verify that the `addToCart` method in the store is called with the correct product
expect(addToCartMock).toHaveBeenCalledTimes(1);
expect(addToCartMock).toHaveBeenCalledWith({ item: product });
});