Skip to content

Commit

Permalink
Merge pull request #12 from alexmarqs/chore/integrate-e2e-tests
Browse files Browse the repository at this point in the history
chore: integrate e2e tests web
  • Loading branch information
alexmarqs authored Jan 26, 2025
2 parents f42453a + d258661 commit e2cea6e
Show file tree
Hide file tree
Showing 14 changed files with 805 additions and 24 deletions.
41 changes: 38 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ on:
branches:
- main
pull_request:
types: [opened, synchronize]
branches:
- main

jobs:
check-and-test:
Expand Down Expand Up @@ -40,6 +41,40 @@ jobs:
- name: Run tests
run: npm run test

web-e2e-tests:
name: Web E2E Tests
timeout-minutes: 15
runs-on: ubuntu-latest
needs: [check-and-test]
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 2

# automated-testing:
# name: E2E Testing
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'

- name: Install dependencies for web
run: npm install --filter=@tech-companies-portugal/web

- name: Install Playwright browsers
run: npx playwright install chromium --with-deps

- name: Run Playwright tests
run: npm run test:e2e:web

- name: Upload test results
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: playwright-report-web
path: ./apps/web/playwright-report/
retention-days: 2

3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The main goal is to provide a better way to explore tech companies in Portugal.
## Monorepo structure 📦

- `apps/web`: The main web app
- `packages/analytics`: Analytics package
- `packages/analytics`: Analytics utils
- `tooling/typescript`: TypeScript configuration
- `tooling/tailwind`: Tailwind configuration

Expand All @@ -23,6 +23,7 @@ The main goal is to provide a better way to explore tech companies in Portugal.
- [TypeScript](https://www.typescriptlang.org/)
- [Shadcn UI](https://ui.shadcn.com) - UI components
- [Vercel](https://vercel.com/) - Hosting and CI/CD
- [Playwright](https://playwright.dev/) - E2E testing
- [Biome](https://biomejs.dev/) - Formatting and linting
- [Motion](https://motion.dev/) - Animation library
- [Nuqs](https://nuqs.47ng.com) - URL query state management (client and server support + some other cool features out of the box)
Expand Down
7 changes: 7 additions & 0 deletions apps/web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

# Playwright
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
16 changes: 10 additions & 6 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@
"lint": "biome check . --write",
"format": "biome format --write .",
"check-types": "tsc --noEmit",
"clean": "git clean -xdf .next .turbo node_modules",
"test": "echo 'No tests to run'"
"clean": "git clean -xdf .next .turbo node_modules playwright-report test-results",
"test": "echo 'No tests to run'",
"test:e2e": "start-server-and-test start http://localhost:3000 'playwright test'",
"test:e2e:ui": "start-server-and-test start http://localhost:3000 'playwright test --ui'"
},
"dependencies": {
"@radix-ui/react-collapsible": "1.1.2",
"@radix-ui/react-label": "2.1.1",
"@radix-ui/react-select": "2.1.4",
"@radix-ui/react-slot": "1.1.1",
"@tech-companies-portugal/analytics": "*",
"cheerio": "1.0.0-rc.12",
"class-variance-authority": "0.7.0",
"clsx": "2.1.0",
Expand All @@ -34,17 +37,18 @@
"slugify": "1.6.6",
"tailwind-merge": "2.2.1",
"tailwindcss-animate": "1.0.7",
"use-debounce": "10.0.4",
"@tech-companies-portugal/analytics": "*"
"use-debounce": "10.0.4"
},
"devDependencies": {
"@tech-companies-portugal/typescript": "*",
"@playwright/test": "1.50.0",
"@tech-companies-portugal/tailwind": "*",
"@tech-companies-portugal/typescript": "*",
"@types/node": "^20",
"@types/react": "19.0.2",
"@types/react-dom": "19.0.2",
"autoprefixer": "^10.0.1",
"postcss": "^8",
"start-server-and-test": "2.0.10",
"tailwindcss": "^3.3.0",
"typescript": "^5"
},
Expand All @@ -53,4 +57,4 @@
"@types/react-dom": "19.0.2",
"react-is": "19.0.0"
}
}
}
86 changes: 86 additions & 0 deletions apps/web/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { defineConfig, devices } from "@playwright/test";

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./tests",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",

timeout: 30_000,

expect: {
timeout: 15_000,
},

/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:3000",

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
},

/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},

// {
// name: "firefox",
// use: { ...devices["Desktop Firefox"] },
// },

// {
// name: "webkit",
// use: { ...devices["Desktop Safari"] },
// },

/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },

/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],

/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});
5 changes: 4 additions & 1 deletion apps/web/src/components/CompaniesHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { AnimatedCompaniesFeatures } from "./ui/AnimatedCompaniesFeatures";

export default function CompaniesHeader() {
return (
<section className="relative w-full overflow-hidden py-12 text-center">
<section
className="relative w-full overflow-hidden py-12 text-center"
data-testid="companies-header"
>
<div className="absolute inset-0 z-0 bg-background">
<Image
src={bgHeader}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/CompaniesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default function CompaniesList({
filteredCompanies={filteredCompanies}
/>
</motion.div>
<div className="flex-1 space-y-4">
<div className="flex-1 space-y-4" data-testid="companies-list">
{paginatedCompanies.map((company, index) => (
<motion.div
key={company.slug}
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/components/CompaniesListFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export default function CompaniesListFooter({
const isNextDisabled = currentPage === totalPages;

return (
<div className="flex items-center justify-between gap-2">
<div
className="flex items-center justify-between gap-2"
data-testid="companies-list-footer"
>
<div className="flex basis-1/2 justify-end text-sm text-muted-foreground h-9">
<Badge variant="outline" className="rounded-none bg-white px-1 gap-1">
Page {currentPage} of {totalPages}
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/components/CompanyItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default function CompanyItem({
<RetroContainer
variant={isFeatured ? "featured" : "default"}
className="w-full relative"
data-testid="company-item"
>
<Link
className={cn(
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { Button } from "./ui/button";

export default function Navbar() {
return (
<header className="bg-background shadow-sm sticky top-0 z-10 py-2 font-mono font-semibold">
<header
className="bg-background shadow-sm sticky top-0 z-10 py-2 font-mono font-semibold"
data-testid="navbar"
>
<div className="container mx-auto flex h-full items-center justify-between flex-wrap px-3">
<Link href="/" className="flex items-center gap-1 flex-shrink-0">
<Image
Expand Down
43 changes: 43 additions & 0 deletions apps/web/tests/homepage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { expect, test } from "@playwright/test";

test.describe("Homepage e2e tests", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
});

test("Check if all relevant elements are visible in the page", async ({
page,
}) => {
// check if navbar is visible
await expect(page.getByTestId("navbar")).toBeVisible();

// check if companies header is visible
await expect(page.getByTestId("companies-header")).toBeVisible();

// check if companies list is visible
await expect(page.getByTestId("companies-list")).toBeVisible();

// check if there are more than 1 company items
const companyItems = await page.getByTestId("company-item").all();
expect(companyItems.length).toBeGreaterThan(1);

// check if filters are visible and reset button is disabled
await expect(
page.getByRole("combobox", { name: "Category" }),
).toBeVisible();
await expect(
page.getByRole("textbox", { name: "Search term" }),
).toBeVisible();
await expect(
page.getByRole("combobox", { name: "Location" }),
).toBeVisible();
await expect(
page.getByRole("button", { name: "Reset filters" }),
).toBeDisabled();

// check if companies list footer is visible and has the correct text
const companiesListFooter = page.getByTestId("companies-list-footer");
await expect(companiesListFooter).toBeVisible();
await expect(companiesListFooter.getByText("Page 1 of")).toBeVisible();
});
});
Loading

0 comments on commit e2cea6e

Please sign in to comment.