Skip to content
Closed
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
107 changes: 107 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
name: Test

on:
pull_request:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: 0.163.3
extended: true

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Load documentation content
run: make prepare

- name: Build site
run: make production-build

- name: Upload build output
uses: actions/upload-artifact@v4
if: always()
with:
name: build-output
path: public/
retention-days: 5

test:
runs-on: ubuntu-latest
needs: build

steps:
- uses: actions/checkout@v4

- name: Set up Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: 0.163.3
extended: true

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'

- name: Install dependencies
run: npm ci

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

- name: Load documentation content
run: make prepare

- name: Run E2E tests
run: npm run test:e2e

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 7

lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Check for trailing whitespace
run: |
if grep -r '[[:space:]]$' --include='*.md' --include='*.toml' --include='*.yaml' --include='*.yml' .; then
echo "❌ Found trailing whitespace"
exit 1
fi
echo "✅ No trailing whitespace"

- name: Validate YAML files
run: |
for file in .github/workflows/*.yml; do
echo "Checking $file..."
if ! python3 -c "import yaml; yaml.safe_load(open('$file'))" 2>/dev/null; then
echo "❌ Invalid YAML: $file"
exit 1
fi
done
echo "✅ All YAML files valid"
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ node_modules/
public*/
resources/

# Content from goharbor/harbor repo
# Content from goharbor/harbor repo (generated at build time)
content/docs/
content/cli-docs/
generated/

# Link checker artifacts
Expand Down Expand Up @@ -46,3 +47,8 @@ Thumbs.db
*.un~
.idea/
.hugo_build.lock

# Playwright test artifacts #
######################
test-results/
playwright-report/
29 changes: 29 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
FROM debian:bookworm-slim

# Install dependencies
RUN apt-get update && apt-get install -y \
wget \
ca-certificates \
nodejs \
npm && \
rm -rf /var/lib/apt/lists/*

# Install Hugo 0.163.3 extended
RUN wget -q https://github.com/gohugoio/hugo/releases/download/v0.163.3/hugo_extended_0.163.3_linux-amd64.tar.gz && \
tar xzf hugo_extended_0.163.3_linux-amd64.tar.gz -C /usr/local/bin && \
rm hugo_extended_0.163.3_linux-amd64.tar.gz && \
hugo version

WORKDIR /site

# Copy project files
COPY . /site/

# Install npm dependencies
RUN npm install

# Expose Hugo server port
EXPOSE 1313

# Default command: run Hugo server
CMD ["hugo", "server", "--bind", "0.0.0.0", "--buildDrafts", "--buildFuture", "--disableFastRender"]
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@ check-internal-links: clean build link-checker-setup run-checker

check-all-links: clean build link-checker-setup
bin/htmltest --conf .htmltest.external.yml

test:
npm run test:e2e

test-ui:
npm run test:e2e:ui
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,28 @@ make serve

This starts up the local Hugo server on http://localhost:1313. As you make changes, the site refreshes automatically in your browser.

## Testing

### Running E2E Tests

The website includes Playwright E2E tests that validate rendering and functionality:

```sh
make test
```

To run tests in interactive UI mode:

```sh
make test-ui
```

Tests are located in the [`e2e/`](./e2e) directory and validate:
- Admonition shortcodes render correctly
- Custom output formats (e.g., `_redirects` for Netlify)
- CSS pipeline and styling
- Site configuration changes

## Checking links

To run the link checker for the Harbor website:
Expand Down
4 changes: 2 additions & 2 deletions config.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
title = "Harbor"
baseURL = "https://goharbor.io"
disableKinds = ["taxonomy", "taxonomyTerm"]
disableKinds = ["taxonomy", "term"]
ignoreFiles = ["README.md"]

[params]
Expand Down Expand Up @@ -163,7 +163,7 @@ weight = 4
home = [ "HTML", "REDIRECTS" ]

[mediaTypes."text/netlify"]
delimiter = ""
suffixes = []

[outputFormats.REDIRECTS]
mediaType = "text/netlify"
Expand Down
83 changes: 83 additions & 0 deletions e2e/breaking-changes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { test, expect } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';

test.describe('Hugo breaking changes are fixed', () => {
test('admonition shortcodes render HTML correctly (markdownify fix)', async ({ page }) => {
// Use a docs page known to have admonitions
await page.goto('/docs/2.1.0/administration/configuring-replication/');

// Check that admonition elements exist
const admonitions = page.locator('.admonition');
await expect(admonitions).not.toHaveCount(0);

// Verify content is rendered, not raw markdown
const firstAdmonition = admonitions.first().locator('.content');
const innerHTML = await firstAdmonition.innerHTML();

// If markdownify failed, we'd see raw markdown syntax like `**bold**` or `[link](url)`
expect(innerHTML).not.toMatch(/\*\*/);
expect(innerHTML).not.toMatch(/\[.*\]\(.*\)/);

// Verify there's actual rendered text
expect(innerHTML.length).toBeGreaterThan(0);
});

test('disableKinds "term" produces no taxonomy pages', async () => {
// The disableKinds config prevents taxonomy/term pages from being built.
// Verify the built output has no taxonomy index pages.
const publicDir = path.join(process.cwd(), 'public');

const taxonomyDir = path.join(publicDir, 'tags');
const termDir = path.join(publicDir, 'categories');

expect(fs.existsSync(taxonomyDir)).toBe(false);
expect(fs.existsSync(termDir)).toBe(false);
});

test('custom output format _redirects is generated', async () => {
const publicDir = path.join(process.cwd(), 'public');
const redirectsFile = path.join(publicDir, '_redirects');

expect(fs.existsSync(redirectsFile)).toBe(true);

const content = fs.readFileSync(redirectsFile, 'utf-8');
expect(content.length).toBeGreaterThan(0);

// Should contain redirect rules (format: /source /destination 301)
expect(content).toMatch(/^\/.+\s+/m);
});

test('CSS pipeline processes without errors (css.Sass)', async ({ page }) => {
await page.goto('/');

// Verify stylesheet link exists in head (link elements are never "visible" in Playwright)
const styleLink = page.locator('link[rel="stylesheet"]').first();
await expect(styleLink).toHaveCount(1);

const href = await styleLink.getAttribute('href');
expect(href).toBeTruthy();
expect(href).toContain('.css');

// Verify the stylesheet loaded by checking computed styles are applied
const body = page.locator('body');
const computedStyle = await body.evaluate(el =>
window.getComputedStyle(el).backgroundColor
);
expect(computedStyle).not.toBe('');
});

test('built CSS is fingerprinted and valid', async () => {
// In production build (hugo build), CSS should be fingerprinted
const publicDir = path.join(process.cwd(), 'public');
const cssDir = path.join(publicDir, 'css');

const files = fs.readdirSync(cssDir);
const fingerprinted = files.find(f => f.match(/\.[a-f0-9]{64}\.css$/));

expect(fingerprinted).toBeDefined();

const content = fs.readFileSync(path.join(cssDir, fingerprinted!), 'utf-8');
expect(content.length).toBeGreaterThan(100000);
});
});
4 changes: 2 additions & 2 deletions layouts/partials/admonition.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
<div class="content">
{{ with .title }}
<p class="title">
{{ . | markdownify }}
{{ . | string | markdownify }}
</p>
{{ end }}

{{ .content | markdownify }}
{{ .content | string | markdownify }}
</div>
</div>
</div>
Expand Down
9 changes: 2 additions & 7 deletions layouts/partials/css.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
{{- $inServerMode := site.IsServer }}
{{- $includePaths := (slice "node_modules") }}
{{- $sass := "sass/style.sass" }}
{{- $cssOutput := "css/style.css" }}
{{- $devOpts := (dict "targetPath" $cssOutput "includePaths" $includePaths "enableSourceMap" true) }}
{{- $prodOpts := (dict "targetPath" $cssOutput "includePaths" $includePaths "outputStyle" "compressed") }}
{{- $cssOpts := cond $inServerMode $devOpts $prodOpts }}
{{- $css := resources.Get $sass | resources.ExecuteAsTemplate $sass . | toCSS $cssOpts }}
{{- if $inServerMode }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}">
{{- else }}
{{- $cssOpts := $prodOpts }}
{{- $css := resources.Get $sass | resources.ExecuteAsTemplate $sass . | css.Sass $cssOpts }}
{{- $prodCss := $css | postCSS | fingerprint }}
<link rel="stylesheet" href="{{ $prodCss.RelPermalink }}" integrity="{{ $prodCss.Data.Integrity }}">
{{- end }}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" />
1 change: 0 additions & 1 deletion layouts/shortcodes/version.html

This file was deleted.

2 changes: 1 addition & 1 deletion netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ publish = "public"
command = "make production-build"

[build.environment]
HUGO_VERSION = "0.74.0"
HUGO_VERSION = "0.163.3"

[context.deploy-preview]
command = "make preview-build"
Expand Down
14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
{
"dependencies": {
"autoprefixer": "^9.7.4",
"bulma": "^0.8.0",
"postcss-cli": "^7.1.2"
"autoprefixer": "^10.5.2",
"bulma": "^1.0.4",
"postcss-cli": "^11.0.1"
},
"devDependencies": {
"@playwright/test": "^1.48.0"
},
"scripts": {
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:debug": "playwright test --debug"
}
}
28 changes: 28 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './e2e',
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:1313',
trace: 'on-first-retry',
},

projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chromium'] },
},
],

webServer: {
command: 'hugo server --buildDrafts --buildFuture',
url: 'http://localhost:1313',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
});
Loading