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
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI - PR Tests

on:
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v5

- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: 24
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Start Vite dev server
run: |
nohup npm run dev:ci > vite.log 2>&1 &
npx wait-on http://localhost:5173
env:
CI: true

- name: Run Puppeteer tests (test:ci)
run: npm run test:ci
env:
CI: true

- name: Display coverage
run: |
npm run collect:coverage:text
10 changes: 4 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
"type": "module",
"scripts": {
"dev": "vite",
"dev:ci": "CI=true VITE_COVERAGE=true vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"test": "vitest",
"preview": "vite preview",
"serve": "json-server --watch data/data.json --routes data/routes.json --port 3001",
"serve:dev": "npm-run-all --parallel serve dev",
"collect:coverage": "nyc report --reporter=html --report-dir=coverage --temp-dir=coverage",
"test:ci": "node scripts/run-tests-ci.js"
"test:ci": "node scripts/run-tests-ci.js",
"collect:coverage:html": "npx nyc report --reporter=html --report-dir=coverage",
"collect:coverage:lcov": "npx nyc report --reporter=lcov --report-dir=coverage",
"collect:coverage:text": "npx nyc report --reporter=text --report-dir=coverage"
},
"dependencies": {
"@radix-ui/react-avatar": "^1.1.4",
Expand All @@ -32,6 +35,7 @@
"tailwind-merge": "^3.2.0",
"tailwindcss": "^4.1.4",
"tw-animate-css": "^1.2.5",
"twd-js": "^0.8.0",
"zustand": "^5.0.3"
},
"devDependencies": {
Expand All @@ -54,7 +58,6 @@
"npm-run-all": "^4.1.5",
"nyc": "^17.1.0",
"puppeteer": "^24.29.1",
"twd-js": "^0.8.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.26.1",
"vite": "^6.3.1",
Expand Down
25 changes: 22 additions & 3 deletions scripts/run-tests-ci.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import fs from 'fs';
import path from 'path';
import puppeteer from "puppeteer";
import { reportResults } from 'twd-js/runner-ci';

let __dirname = path.resolve();

const browser = await puppeteer.launch({
headless: true,
args: ['--lang=es-ES,es'],
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
await page.emulateTimezone('Europe/Madrid');
console.time('Total Test Time');
try {
// Navigate to your development server
Expand Down Expand Up @@ -40,7 +43,23 @@ try {
// Display results in console
reportResults(handlers, testStatus);

const coverage = await page.evaluate(() => window.__coverage__)
const coverage = await page.evaluate(() => window.__coverage__);
if (coverage) {
console.log('Collecting code coverage data...');
const coverageDir = path.resolve(__dirname, './coverage');
const nycDir = path.resolve(__dirname, './.nyc_output');
if (!fs.existsSync(nycDir)) {
fs.mkdirSync(nycDir);
}
if (!fs.existsSync(coverageDir)) {
fs.mkdirSync(coverageDir);
}
const coveragePath = path.join(nycDir, 'out.json');
fs.writeFileSync(coveragePath, JSON.stringify(coverage));
console.log(`Code coverage data written to ${coveragePath}`);
} else {
console.log('No code coverage data found.');
}

// Exit with appropriate code
const hasFailures = testStatus.some(test => test.status === 'fail');
Expand Down
4 changes: 1 addition & 3 deletions src/twd-tests/helloWorld.twd.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ describe("Hello World Page", () => {
counterButton.should("have.text", "Count is 2");

await userEvent.click(counterButton.el);
console.log(counterButton.el.textContent);

counterButton.should("have.text", "Count is 3");
});
});
});
62 changes: 30 additions & 32 deletions src/twd-tests/todoList.twd.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ describe("Todo List Page", () => {
});
await twd.visit("/todos");
await twd.waitForRequest("getTodoList");
const todoList = await twd.getAll("[data-testid='todo-item']");
expect(todoList).to.have.length(2);
const todo1Title = await twd.get("[data-testid='todo-title-1']");
todo1Title.should("have.text", "Learn TWD");
const todo2Title = await twd.get("[data-testid='todo-title-2']");
Expand All @@ -32,34 +30,6 @@ describe("Todo List Page", () => {
todo2Date.should("have.text", "Date: 2024-12-25");
});

it("should delete a todo", async () => {
await twd.mockRequest("deleteTodo", {
method: "DELETE",
url: "/api/todos/1",
response: null,
status: 200,
});
await twd.mockRequest("getTodoList", {
method: "GET",
url: "/api/todos",
response: todoListMock,
status: 200,
});
await twd.visit("/todos");
const deleteButton = await twd.get("[data-testid='delete-todo-1']");
await twd.mockRequest("getTodoList", {
method: "GET",
url: "/api/todos",
response: todoListMock.filter((todo) => todo.id !== "1"),
status: 200,
});
await userEvent.click(deleteButton.el);
await twd.waitForRequest("deleteTodo");
await twd.waitForRequest("getTodoList");
const todoList = await twd.getAll("[data-testid='todo-item']");
expect(todoList).to.have.length(1);
});

it("should create a todo", async () => {
await twd.mockRequest("createTodo", {
method: "POST",
Expand All @@ -75,6 +45,8 @@ describe("Todo List Page", () => {
});
await twd.visit("/todos");
await twd.waitForRequest("getTodoList");
const noTodosMessage = await twd.get("[data-testid='no-todos-message']");
noTodosMessage.should("be.visible");
await twd.mockRequest("getTodoList", {
method: "GET",
url: "/api/todos",
Expand All @@ -83,8 +55,6 @@ describe("Todo List Page", () => {
],
status: 200,
});
const noTodosMessage = await twd.get("[data-testid='no-todos-message']");
noTodosMessage.should("be.visible");
const title = await twd.get("input[name='title']");
await userEvent.type(title.el, "Test Todo");
const description = await twd.get("input[name='description']");
Expand All @@ -103,4 +73,32 @@ describe("Todo List Page", () => {
const todoList = await twd.getAll("[data-testid='todo-item']");
expect(todoList).to.have.length(1);
});

it("should delete a todo", async () => {
await twd.mockRequest("deleteTodo", {
method: "DELETE",
url: "/api/todos/1",
response: null,
status: 200,
});
await twd.mockRequest("getTodoList", {
method: "GET",
url: "/api/todos",
response: todoListMock,
status: 200,
});
await twd.visit("/todos");
const deleteButton = await twd.get("[data-testid='delete-todo-1']");
await twd.mockRequest("getTodoList", {
method: "GET",
url: "/api/todos",
response: todoListMock.filter((todo) => todo.id !== "1"),
status: 200,
});
await userEvent.click(deleteButton.el);
await twd.waitForRequest("deleteTodo");
await twd.waitForRequest("getTodoList");
const todoList = await twd.getAll("[data-testid='todo-item']");
expect(todoList).to.have.length(1);
});
});
4 changes: 3 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import path from "path"
import tailwindcss from "@tailwindcss/vite"
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// add plugin for code coverage
import istanbul from 'vite-plugin-istanbul';

// https://vite.dev/config/
export default defineConfig({
plugins: [
react(),
tailwindcss(),
// configure istanbul plugin
istanbul({
include: 'src/**/*',
exclude: ['node_modules', 'tests/'],
Expand All @@ -27,4 +29,4 @@ export default defineConfig({
ignored: ["**/data/data.json", "**data/routes.json"],
},
},
})
})