Skip to content

Commit 8a50803

Browse files
authored
Merge pull request #271 from OpenElements/next-js-tests
Next js tests
2 parents 5d3d3ea + 32dc155 commit 8a50803

10 files changed

Lines changed: 78 additions & 122 deletions

File tree

.husky/pre-commit

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
#!/bin/sh
2-
. "$(dirname "$0")/_/husky.sh"
32

43
set -e # die on error
54

6-
pnpm run lint
5+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
6+
7+
if command -v pnpm >/dev/null 2>&1; then
8+
cd "$REPO_ROOT"
9+
pnpm run lint
10+
else
11+
echo "pnpm not found. Skipping lint." >&2
12+
fi

README.md

Lines changed: 35 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,65 @@
11
# Open Elements Website
22

3-
This repo contains the website of Open Elements.
4-
The website is still work in progress.
5-
In future the website will be available at https://www.open-elements.de and https://www.open-elements.com.
3+
This repository contains the Open Elements website.
64

7-
Netlify status of English page:
5+
## Architecture (2026)
86

9-
[![Netlify status of English page](https://api.netlify.com/api/v1/badges/0a7875a4-d4ba-4358-8616-87200dcbe7c5/deploy-status)](https://app.netlify.com/sites/open-elements-en/deploys)
7+
The project is now a Next.js application with App Router, Tailwind CSS, and `next-intl` for i18n. Legacy Hugo content and templates are still kept in the repo for migration and historical content.
108

11-
Netlify status of German page:
9+
### Runtime layers
1210

13-
[![Netlify status of German page](https://api.netlify.com/api/v1/badges/935f5408-eef5-4889-9cb6-ee55a0990a0f/deploy-status)](https://app.netlify.com/sites/open-elements-de/deploys)
11+
- **Next.js App (primary)**
12+
- App Router pages and layouts in `src/app`.
13+
- UI components in `src/components`.
14+
- Shared utilities in `src/lib`, data in `src/data`, types in `src/types`.
15+
- Styling via Tailwind CSS and `src/app/globals.css`.
1416

17+
- **Internationalization**
18+
- `next-intl` routing and helpers in `src/i18n`.
19+
- Translation messages in `locales`.
1520

16-
## Building the website
21+
- **Legacy Hugo content (migrating)**
22+
- Markdown content in `content`.
23+
- Hugo templates in `src/layouts`.
24+
- Hugo configuration in `config.toml`.
25+
- Built static artifacts live in `public` (do not edit manually).
1726

18-
Since the page is based on Hugo and React we use `npm-run-all` to execute several dev executions in parallel.
19-
Therefore you need to install `npm-run-all` as a dev dependency:
27+
- **Web components**
28+
- Custom elements live in `react-src` and are bundled via `react-src/build.mjs` into `public/js`.
2029

21-
```
22-
npm install --save-dev npm-run-all
23-
```
24-
25-
26-
The project is based on [Hugo](https://gohugo.io/) and you need to [install Hugo](https://gohugo.io/installation/) to build the website.
27-
Once Hugo is installed you can host the website on localhost by executing to following command from the root folder of the repository:
28-
29-
```
30-
hugo serve
31-
```
30+
- **E2E tests**
31+
- Playwright specs in `tests/e2e`.
3232

33-
While the process is running the English (default) version of the website can be reached at http://localhost:1313/ and the German can be reached at http://localhost:1314/.
33+
## Development
3434

35-
## Adding Tailwind CSS
35+
### Requirements
3636

37-
### 1-Install Tailwind CSS
37+
- Node.js 22
38+
- pnpm 10
3839

39-
Install tailwindcss via npm, and create your tailwind.config.js file in the root folder.
40+
### Install dependencies
4041

4142
```
42-
npm install -D tailwindcss
43-
npx tailwindcss init
43+
pnpm install
4444
```
4545

46-
### 2-Configure your template paths
47-
48-
Add the paths to all of your template files in your tailwind.config.js file.
46+
### Run locally
4947

5048
```
51-
content: [
52-
"content/**/*.md", "layouts/**/*.html"
53-
],
49+
pnpm run dev
5450
```
5551

56-
### 3-Add the Tailwind directives to your CSS
57-
Create 'input.css' file in the root folder and add the @tailwind directives for each of Tailwind’s layers to your input CSS file.
58-
59-
```
60-
@tailwind base;
61-
@tailwind components;
62-
@tailwind utilities;
63-
```
52+
The app is available at http://localhost:3000.
6453

65-
### 4-Code snippet for Package.json
66-
67-
Add the following code in 'Package.json'
54+
### Build & start
6855

6956
```
70-
"scripts": {
71-
"dev:css": "npx tailwindcss -i input.css -o assets/css/style.css -w",
72-
"dev:hugo": "hugo server",
73-
"dev": "run-p dev:*",
74-
"build:css": "NODE_ENV=production npx tailwindcss -i input.css -o assets/css/style.css -m",
75-
"build:hugo": "hugo",
76-
"build": "run-s build:*"
77-
},
57+
pnpm run build
58+
pnpm run start
7859
```
7960

80-
### 5-Dev environment
81-
For development run the following command in terminal.
82-
```
83-
npm run dev
84-
```
61+
### E2E tests
8562

86-
### 6-Production
87-
For production ready css, run the following command in terminal.
8863
```
89-
npm run build
64+
pnpm run test:e2e
9065
```

eslint.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default defineConfig([
1111
"build/**",
1212
"next-env.d.ts",
1313
"content/**",
14-
"src/react-src/**"
14+
"public/**",
15+
"react-src/**"
1516
])
1617
]);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"dev": "next dev",
88
"build": "next build",
99
"start": "next start",
10-
"lint": "next lint",
10+
"lint": "eslint .",
1111
"test:e2e": "playwright test",
1212
"prepare": "husky"
1313
},

react-src/maven-prs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export default function MavenPRs({status}: { status?: string }) {
6969
}
7070

7171
fetchPRs();
72-
}, []);
72+
}, [status]);
7373

7474
if (!prs) return <div>Keine Pull Requests gefunden</div>;
7575

src/app/[locale]/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { notFound } from 'next/navigation';
44
import { routing } from '@/i18n/routing';
55
import type { Metadata } from 'next'
66
import { Montserrat } from 'next/font/google'
7+
import Script from 'next/script'
78
import '../globals.css'
89
import Navbar from '@/components/Navbar'
910
import Footer from '@/components/Footer'
@@ -85,7 +86,7 @@ export default async function LocaleLayout({
8586
<main>{children}</main>
8687
<Footer locale={locale} />
8788
</div>
88-
<script src="https://code.iconify.design/2/2.2.1/iconify.min.js"></script>
89+
<Script src="https://code.iconify.design/2/2.2.1/iconify.min.js" strategy="afterInteractive" />
8990
</NextIntlClientProvider>
9091
</body>
9192
</html>

tests/e2e/about.spec.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ test.describe('About Page', () => {
1010
for (const locale of locales) {
1111
test(`loads about page correctly for ${locale}`, async ({ page }) => {
1212
await page.goto(localePath(locale, 'about'));
13-
14-
// Page should load successfully
13+
1514
await expect(page).toHaveURL(/\/about/);
16-
17-
// Should have main content
15+
1816
await expect(page.locator('main')).toBeVisible();
19-
20-
// Should maintain locale in URL
17+
2118
if (locale === 'de') {
2219
await expect(page).toHaveURL(/\/de\/about/);
2320
} else {
@@ -27,15 +24,12 @@ test.describe('About Page', () => {
2724

2825
test(`about page navigation works for ${locale}`, async ({ page }) => {
2926
await page.goto(localePath(locale));
30-
31-
// Find and click about link in navigation
27+
3228
const aboutLink = page.locator('nav a[href*="about"]').first();
3329
await aboutLink.click();
34-
35-
// Should navigate to about page
30+
3631
await expect(page).toHaveURL(/\/about/);
37-
38-
// Should maintain locale
32+
3933
if (locale === 'de') {
4034
await expect(page).toHaveURL(/\/de\/about/);
4135
}

tests/e2e/accessibility.spec.ts

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,56 +10,48 @@ test.describe('Accessibility', () => {
1010
for (const locale of locales) {
1111
test(`home page has proper heading hierarchy for ${locale}`, async ({ page }) => {
1212
await page.goto(localePath(locale));
13-
14-
// Should have exactly one h1
13+
1514
const h1Count = await page.locator('h1').count();
1615
expect(h1Count).toBe(1);
17-
18-
// H1 should have text content
16+
1917
const h1Text = await page.locator('h1').first().textContent();
2018
expect(h1Text?.trim().length).toBeGreaterThan(0);
2119
});
2220

2321
test(`navigation has proper ARIA landmarks for ${locale}`, async ({ page }) => {
2422
await page.goto(localePath(locale));
25-
26-
// Should have navigation landmark
23+
2724
const nav = page.locator('nav, [role="navigation"]');
2825
await expect(nav).toBeVisible();
29-
30-
// Should have main landmark
26+
3127
const main = page.locator('main, [role="main"]');
3228
await expect(main).toBeVisible();
3329
});
3430

3531
test(`all images have alt text for ${locale}`, async ({ page }) => {
3632
await page.goto(localePath(locale));
37-
38-
// Get all images
33+
3934
const images = page.locator('img');
4035
const count = await images.count();
41-
42-
// Check each image has alt attribute
36+
4337
for (let i = 0; i < count; i++) {
4438
const img = images.nth(i);
4539
const alt = await img.getAttribute('alt');
46-
// Alt can be empty string for decorative images, but must exist
40+
4741
expect(alt).not.toBeNull();
4842
}
4943
});
5044

5145
test(`interactive elements are keyboard accessible for ${locale}`, async ({ page }) => {
5246
await page.goto(localePath(locale));
53-
54-
// All links should be keyboard accessible
47+
5548
const links = page.locator('a[href]');
5649
const linkCount = await links.count();
5750

5851
if (linkCount > 0) {
5952
const firstLink = links.first();
6053
await expect(firstLink).toBeVisible();
61-
62-
// Link should be focusable
54+
6355
await firstLink.focus();
6456
await expect(firstLink).toBeFocused();
6557
}

tests/e2e/contact.spec.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ test.describe('Contact Page', () => {
1010
for (const locale of locales) {
1111
test(`loads contact page correctly for ${locale}`, async ({ page }) => {
1212
await page.goto(localePath(locale, 'contact'));
13-
14-
// Page should load successfully
13+
1514
await expect(page).toHaveURL(/\/contact/);
16-
17-
// Should have main content
15+
1816
await expect(page.locator('main')).toBeVisible();
19-
20-
// Should maintain locale in URL
17+
2118
if (locale === 'de') {
2219
await expect(page).toHaveURL(/\/de\/contact/);
2320
} else {
@@ -27,17 +24,14 @@ test.describe('Contact Page', () => {
2724

2825
test(`contact page navigation works for ${locale}`, async ({ page }) => {
2926
await page.goto(localePath(locale));
30-
31-
// Find and click contact link in navigation
27+
3228
const contactLink = page.locator('nav a[href*="contact"]').first();
3329

3430
if (await contactLink.count() > 0) {
3531
await contactLink.click();
36-
37-
// Should navigate to contact page
32+
3833
await expect(page).toHaveURL(/\/contact/);
39-
40-
// Should maintain locale
34+
4135
if (locale === 'de') {
4236
await expect(page).toHaveURL(/\/de\/contact/);
4337
}

tests/e2e/home.spec.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,33 @@ test.describe('Home Page', () => {
1010
for (const locale of locales) {
1111
test(`loads home page correctly for ${locale}`, async ({ page }) => {
1212
await page.goto(localePath(locale));
13-
14-
// Page should load successfully
13+
1514
await expect(page).toHaveTitle(/Open Elements/i);
16-
17-
// Should have main navigation
15+
1816
await expect(page.locator('nav')).toBeVisible();
19-
20-
// Should have main content
17+
2118
await expect(page.locator('main')).toBeVisible();
2219
});
2320

2421
test(`home page has proper meta tags for ${locale}`, async ({ page }) => {
2522
await page.goto(localePath(locale));
26-
27-
// Should have meta description
23+
2824
const metaDescription = page.locator('meta[name="description"]');
2925
await expect(metaDescription).toHaveAttribute('content', /.+/);
30-
31-
// Should have viewport meta tag
26+
3227
const viewport = page.locator('meta[name="viewport"]');
3328
await expect(viewport).toHaveAttribute('content', /.+/);
3429
});
3530

3631
test(`home page is responsive for ${locale}`, async ({ page }) => {
37-
// Test mobile view
32+
3833
await page.setViewportSize({ width: 375, height: 667 });
3934
await page.goto(localePath(locale));
4035
await expect(page.locator('nav')).toBeVisible();
41-
42-
// Test tablet view
36+
4337
await page.setViewportSize({ width: 768, height: 1024 });
4438
await expect(page.locator('nav')).toBeVisible();
45-
46-
// Test desktop view
39+
4740
await page.setViewportSize({ width: 1920, height: 1080 });
4841
await expect(page.locator('nav')).toBeVisible();
4942
});

0 commit comments

Comments
 (0)