Initial commit
This commit is contained in:
15
landing-e2e/eslint.config.mjs
Normal file
15
landing-e2e/eslint.config.mjs
Normal file
@@ -0,0 +1,15 @@
|
||||
import playwright from "eslint-plugin-playwright";
|
||||
import baseConfig from "../eslint.config.mjs";
|
||||
|
||||
export default [
|
||||
playwright.configs["flat/recommended"],
|
||||
...baseConfig,
|
||||
{
|
||||
files: [
|
||||
"**/*.ts",
|
||||
"**/*.js"
|
||||
],
|
||||
// Override or add rules here
|
||||
rules: {}
|
||||
}
|
||||
];
|
||||
40
landing-e2e/playwright.config.mts
Normal file
40
landing-e2e/playwright.config.mts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
|
||||
/**
|
||||
* Written without `@nx/playwright`'s preset on purpose: Playwright loads this
|
||||
* file through Node's ESM loader, and pulling in `@nx/devkit` from there drags
|
||||
* Nx's native binding in under `runSync`, where it fails to initialise. Only
|
||||
* `testDir`, `outputDir` and the web server came from the preset anyway.
|
||||
*/
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const workspaceRoot = resolve(here, '..');
|
||||
const baseURL = process.env['BASE_URL'] || 'http://localhost:4200';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './src',
|
||||
outputDir: resolve(workspaceRoot, 'dist/.playwright/landing-e2e/test-output'),
|
||||
timeout: 60_000,
|
||||
forbidOnly: !!process.env['CI'],
|
||||
retries: process.env['CI'] ? 2 : 0,
|
||||
workers: process.env['CI'] ? 1 : undefined,
|
||||
reporter: [['list']],
|
||||
use: {
|
||||
baseURL,
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
webServer: {
|
||||
command: 'npx nx run landing:serve',
|
||||
url: baseURL,
|
||||
reuseExistingServer: true,
|
||||
timeout: 180_000,
|
||||
cwd: workspaceRoot,
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
],
|
||||
});
|
||||
11
landing-e2e/project.json
Normal file
11
landing-e2e/project.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "landing-e2e",
|
||||
"$schema": "../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"sourceRoot": "landing-e2e/src",
|
||||
"implicitDependencies": [
|
||||
"landing"
|
||||
],
|
||||
"// targets": "to see all targets run: nx show project landing-e2e --web",
|
||||
"targets": {}
|
||||
}
|
||||
66
landing-e2e/src/landing.spec.ts
Normal file
66
landing-e2e/src/landing.spec.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
test.describe('landing', () => {
|
||||
test('renders the hero server-side', async ({ page }) => {
|
||||
const response = await page.goto('/');
|
||||
|
||||
expect(response?.status()).toBe(200);
|
||||
await expect(page.locator('h1')).toContainText('Construimos software');
|
||||
});
|
||||
|
||||
test('serves the page in the language the URL asks for', async ({ page }) => {
|
||||
await page.goto('/?lang=en');
|
||||
|
||||
await expect(page.locator('h1')).toContainText('We build software');
|
||||
await expect(page.locator('html')).toHaveAttribute('lang', 'en');
|
||||
});
|
||||
|
||||
test('server-renders the whole page, not just the top of it', async ({
|
||||
browser,
|
||||
}) => {
|
||||
// With JavaScript off, what is left is exactly what the server sent. The
|
||||
// check is the last section and the footer: a component that throws during
|
||||
// rendering does not merely lose itself, it abandons everything after it,
|
||||
// and the page still answers 200 while quietly ending early. That is how
|
||||
// `pi-carousel` reaching for `document` once cost this page its contact
|
||||
// form server-side.
|
||||
const context = await browser.newContext({ javaScriptEnabled: false });
|
||||
const page = await context.newPage();
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await expect(page.locator('h1')).toContainText('Construimos software');
|
||||
await expect(page.locator('#contacto')).toBeAttached();
|
||||
await expect(page.getByRole('contentinfo')).toContainText('Pireon');
|
||||
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test('answers 404 for an address it does not serve', async ({ page }) => {
|
||||
const response = await page.goto('/no-existe');
|
||||
|
||||
expect(response?.status()).toBe(404);
|
||||
await expect(page.locator('h1')).toContainText('no existe');
|
||||
});
|
||||
|
||||
test('refuses an incomplete contact submission without calling the API', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
await page.locator('#contacto').scrollIntoViewIfNeeded();
|
||||
|
||||
const before = page.url();
|
||||
const submit = page.getByRole('button', { name: 'Enviar' });
|
||||
|
||||
// Enabled only once hydration attached the handler — before that a click
|
||||
// would submit natively and navigate away with the message. The generous
|
||||
// timeout is for the dev server's first compile, not for hydration.
|
||||
await expect(submit).toBeEnabled({ timeout: 30_000 });
|
||||
await submit.click();
|
||||
|
||||
// Nothing navigates and nothing is sent: the form holds the visitor there
|
||||
// rather than spending one of their throttled attempts on a bad payload.
|
||||
await expect(submit).toBeVisible();
|
||||
expect(page.url()).toBe(before);
|
||||
});
|
||||
});
|
||||
24
landing-e2e/tsconfig.json
Normal file
24
landing-e2e/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"outDir": "../dist/out-tsc",
|
||||
"sourceMap": false,
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
"playwright.config.mts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user