Azul Coding
Test Your Site with Playwright in 10 Minutes
tests.spec.ts
// AZUL CODING ---------------------------------------
// Test Your Site with Playwright in 10 Minutes
// https://youtu.be/lyXa9KzUl1E
import { test, expect } from "@playwright/test";
test.describe("Main Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
});
test("should display the correct title", async ({ page }) => {
await expect(page).toHaveTitle("Azul");
});
test("should have working navigation to about page", async ({ page }) => {
await page.getByText("About").click();
await expect(page).toHaveURL(/.*about.html/);
await expect(page.locator("h1")).toContainText("About Us");
});
test("should handle contact form submission", async ({ page }) => {
await page.route("/api/contact", async (route) => {
await route.fulfill({
status: 200,
body: JSON.stringify({ success: true })
});
});
await page.locator('button[type="submit"]').click();
await expect(page.locator("#name-error")).toBeVisible();
await expect(page.locator("#email-error")).toBeVisible();
await page.locator("#name").fill("John Doe");
await page.locator("#email").fill("john@example.com");
await page.locator("#message").fill("Test message");
await page.locator('button[type="submit"]').click();
await expect(page.locator("#success-message")).toBeVisible();
});
test("should handle server error gracefully", async ({ page }) => {
await page.route("/api/contact", async (route) => {
await route.fulfill({
status: 500,
body: JSON.stringify({ error: "Server error" })
});
});
await page.locator("#name").fill("John Doe");
await page.locator("#email").fill("john@example.com");
await page.locator("#message").fill("Test message");
await page.locator('button[type="submit"]').click();
await expect(page.locator("#error-message")).toBeVisible();
});
});
test.describe("About Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/about.html");
});
test("should display the correct title", async ({ page }) => {
await expect(page).toHaveTitle("About Us");
});
test("should have working navigation back to main page", async ({ page }) => {
await page.getByText("Home").click();
await expect(page).toHaveURL(/.*index.html/);
});
});
test.describe("Responsive Design", () => {
test("should display mobile menu on small screens", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto("/");
const mobileMenu = page.locator("#mobile-menu");
await expect(mobileMenu).toBeVisible();
await mobileMenu.click();
const navLinks = page.locator("nav a:first-child");
await expect(navLinks).toBeVisible();
await page.screenshot({ path: "mobile-menu.png" });
});
});
test.describe("Accessibility", () => {
test("should have basic accessibility features", async ({ page }) => {
await page.goto("/");
const h1Count = await page.locator("h1").count();
expect(h1Count).toBe(1);
await page.goto("/about.html");
const aboutH1Count = await page.locator("h1").count();
expect(aboutH1Count).toBe(1);
const images = page.locator("img");
for (const image of await images.all())
await expect(image).toHaveAttribute("alt", /.+/);
});
});
script.js
// AZUL CODING ---------------------------------------
// Test Your Site with Playwright in 10 Minutes
// https://youtu.be/lyXa9KzUl1E
const mobileMenuBtn = document.getElementById("mobile-menu");
const navLinks = document.querySelector(".nav-links");
mobileMenuBtn.addEventListener("click", () => {
navLinks.classList.toggle("active");
});
const contactForm = document.getElementById("contact-form");
contactForm?.addEventListener("submit", async (e) => {
e.preventDefault();
const nameError = document.getElementById("name-error");
const emailError = document.getElementById("email-error");
const successMessage = document.getElementById("success-message");
const errorMessage = document.getElementById("error-message");
nameError.classList.add("hidden");
emailError.classList.add("hidden");
successMessage.classList.add("hidden");
errorMessage.classList.add("hidden");
const name = contactForm.name.value;
const email = contactForm.email.value;
let hasError = false;
if (!name) {
nameError.classList.remove("hidden");
hasError = true;
}
if (!email || !email.includes("@")) {
emailError.classList.remove("hidden");
hasError = true;
}
if (!hasError) {
try {
const response = await fetch("/api/contact", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name, email, message: contactForm.message.value }),
});
if (response.ok) {
successMessage.classList.remove("hidden");
contactForm.reset();
} else {
errorMessage.classList.remove("hidden");
}
} catch (error) {
errorMessage.classList.remove("hidden");
}
}
});
index.html
<!-- AZUL CODING --------------------------------------- -->
<!-- Test Your Site with Playwright in 10 Minutes -->
<!-- https://youtu.be/lyXa9KzUl1E -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Azul Coding</title>
<link rel="stylesheet" href="/assets/styles.css">
<script src="/assets/script.js" defer></script>
</head>
<body>
<header>
<nav>
<div class="container nav-container">
<div class="logo">Azul Coding</div>
<button id="mobile-menu">
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<div class="nav-links">
<a href="/index.html">Home</a>
<a href="/about.html">About</a>
</div>
</div>
</nav>
</header>
<main id="main-content">
<div class="container">
<h1>Welcome to Azul Coding</h1>
<p>This is a sample website that demonstrates various features for Playwright testing.</p>
<section class="contact-section">
<h2>Contact Us</h2>
<form id="contact-form">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" autocomplete="off">
<div id="name-error" class="hidden">Name is required</div>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" autocomplete="off">
<div id="email-error" class="hidden">Valid email is required</div>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" rows="4"></textarea>
</div>
<button type="submit">Send Message</button>
<div id="success-message" class="hidden">Message sent successfully</div>
<div id="error-message" class="hidden">An error occurred. Please try again.</div>
</form>
</section>
</div>
</main>
</body>
</html>
about.html
<!-- AZUL CODING --------------------------------------- -->
<!-- Test Your Site with Playwright in 10 Minutes -->
<!-- https://youtu.be/lyXa9KzUl1E -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About Us</title>
<link rel="stylesheet" href="/assets/styles.css">
<script src="/assets/script.js" defer></script>
</head>
<body>
<header>
<nav>
<div class="container nav-container">
<div class="logo">Azul Coding</div>
<button id="mobile-menu">
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<div class="nav-links">
<a href="/index.html">Home</a>
<a href="/about.html">About</a>
</div>
</div>
</nav>
</header>
<main id="main-content">
<div class="container">
<h1>About Us</h1>
<p>We are a company dedicated to creating great software and helping developers test their applications effectively.</p>
<section class="team-section">
<h2>Our Team</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras tempus vel ipsum in posuere. In massa sem, bibendum a vehicula tempus, efficitur vel ex. In hac habitasse platea dictumst. Praesent id tellus hendrerit, placerat ligula a, elementum dui. Cras suscipit ipsum eget enim condimentum, quis semper ex porttitor.</p>
</section>
<section class="mission-section">
<h2>Our Mission</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras tempus vel ipsum in posuere. In massa sem, bibendum a vehicula tempus, efficitur vel ex. In hac habitasse platea dictumst. Praesent id tellus hendrerit, placerat ligula a, elementum dui.</p>
<img src="/assets/code.jpg" alt="Code on a screen">
</section>
</div>
</main>
</body>
</html>
styles.css
/* AZUL CODING --------------------------------------- */
/* Test Your Site with Playwright in 10 Minutes */
/* https://youtu.be/lyXa9KzUl1E */
* {
font-family: "Inter", "Helvetica", "Arial", sans-serif;
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
line-height: 1.6;
color: white;
background-color: #03506e;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
header {
background: #49c8fc;
}
nav {
padding: 16px 0;
}
nav .nav-container {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 24px;
font-weight: 600;
color: #2c3e50;
}
.nav-links {
display: flex;
gap: 32px;
}
.nav-links a {
text-decoration: none;
color: #2c3e50;
}
.nav-links a:hover {
text-decoration: underline;
}
#mobile-menu {
display: none;
background: none;
border: none;
cursor: pointer;
padding: 5px 10px;
line-height: 0;
}
main {
padding: 32px 0;
}
h1 {
font-size: 32px;
}
h2 {
padding-top: 20px;
}
p {
margin-bottom: 15px;
font-size: 16px;
}
form {
max-width: 500px;
margin: 15px 0;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 10px;
font-weight: 500;
}
input, textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
input:focus, textarea:focus {
outline: 2px solid #49c8fc;
outline-offset: 2px;
}
button {
background-color: #49c8fc;
color: #03506e;
font-weight: 600;
padding: 12px 18px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.5s;
}
button:hover {
background-color: white;
}
[id$="-error"], #error-message, #success-message {
color: #4f0700;
background-color: #ffc4c4;
font-size: 15px;
margin-top: 8px;
padding: 5px 10px;
border-radius: 4px;
}
#success-message {
color: #005925;
background-color: #ebffe2;
}
.hidden {
display: none !important;
}
img {
max-width: 100%;
height: auto;
border-radius: 10px;
margin: 25px 0;
}
@media (max-width: 768px) {
#mobile-menu {
display: block;
}
.nav-links {
display: none;
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
padding: 16px;
flex-direction: column;
gap: 16px;
border: 5px solid #49c8fc;
margin-top: 16px;
}
.nav-links.active {
display: flex;
}
nav .nav-container {
position: relative;
}
h1 {
font-size: 32px;
}
}