Feature Tests
Test your installed guestbook against the expected specifications.
The .feature files on this page are Gherkin specifications. npx y2k-guestbook test runs them with Playwright against your installation of the guestbook:
- A
.featurefile says what should happen. - A step definition is the TypeScript that applies that behaviour in the browser.
Install Playwright
From your Next.js app root:
npm install --save-dev @playwright/test playwright-bdd
npx playwright install chromium
@playwright/test— Runs the scenarios and writes the HTML report.playwright-bdd— Reads the.featurefiles and calls your step definitions.
Confirm the package installations by running:
npm ls @playwright/test playwright-bdd
Setup
Test Reports
A test run writes to a Playwright report folder — open index.html inside to view results:
- A later run will overwrite the previous report.
- A failed scenario keeps recording in the report you can use to debug.
The default folder is features/reports/results/ — in this setup, we'll use this default folder for user tests.
-p admin writes features/reports/admin/ instead of features/reports/results/ — we'll use this custom folder for admin tests.
Support Files
features/support/ contains files that help Playwright run tests by giving them shared context:
features/support/world.ts holds the page, the guestbook URL, and values a later step needs to read:
export class PlaywrightWorld {
page!: Page;
baseUrl: string;
guestbookPath: string;
adminPath: string;
postedName?: string;
constructor() {
this.baseUrl = String(process.env.BASE_URL ?? "http://localhost:3000").replace(/\/$/, "");
this.guestbookPath = process.env.NEXT_PUBLIC_GUESTBOOK_PATH || "/";
this.adminPath = process.env.NEXT_PUBLIC_GUESTBOOK_ADMIN_PATH || "/admin";
}
}
features/support/fixtures.ts gives every step that same world.
features/support/ui.ts holds the shared locators so step definitions click visible names (submit, display name) instead of copying CSS class names.
features/support/seed.ts writes to the database when a scenario needs rows already on the board, and resets rate limits and settings so one run does not leak into the next.
Step Definitions
Example features/step_definitions/ map each Given / When / Then to clicks in the browser, covering the basic functionality of the guestbook, aligning with the Gherkin .feature files below.
You will need to write step definitions for any additional functionality you opt into (notifications, analytics) or any other features that you add.
Running Tests
Run .feature Tests
These tests do not require any admin authentication, so they can be run without any additional setup.
-
Start the guestbook preview server:
npm run dev -
In a second terminal at the app root, call:
To run a specific test in a feature file, append the
--nameflag naming a Scenario. For example:# Writes to features/reports/results/index.html npx y2k-guestbook test features/features/guestbook/guestbook-comments.feature --name "User submits a valid comment"For a specific set of tests under one Rule, append the
--tags @example-rule-nameflag:# Writes to features/reports/results/index.html npx y2k-guestbook test features/features/guestbook/guestbook-comments.feature --tags @submit-commentsFor example, the full
guestbook-nav.featurefile:# Writes to features/reports/results/index.html npx y2k-guestbook test features/features/guestbook/guestbook-nav.feature# Writes to features/reports/results/index.html npx y2k-guestbook test features/features/guestbook
-
Start the guestbook preview server:
npm run dev -
In a second terminal at the app root, call:
-
You'll need to run Chrome in debugging mode. Close all Chrome windows you might have open first, then call (replace the
--user-data-dirpaths with your own):"/c/Program Files/Google/Chrome/Application/chrome.exe" --remote-debugging-port=9222 --remote-allow-origins=* --user-data-dir="C:/Users/username/path/to/your-app/features/support/.auth/chrome-debug"open -a "Google Chrome" --args --remote-debugging-port=9222 --remote-allow-origins=* --user-data-dir="~/path/to/your-app/features/support/.auth/chrome-debug"Confirm that debugging is enabled by using the open Chrome window to view: http://127.0.0.1:9222/json/version
-
Leave the Chrome window that opens up and in a third terminal call:
To run a specific test in a feature file, append the
--nameflag naming a Scenario. For example:# Writes to features/reports/admin/index.html HEADED=1 npx y2k-guestbook test -p admin features/features/admin/admin-settings.feature --name "Admin can update the guestbook title"For a specific set of tests under one Rule, append the
--tags @example-rule-nameflag:# Writes to features/reports/admin/index.html HEADED=1 npx y2k-guestbook test -p admin features/features/admin/admin-nav.feature --tags @mobile-menuFor example, the full
admin-nav.featurefile:# Writes to features/reports/admin/index.html HEADED=1 npx y2k-guestbook test -p admin features/features/admin/admin-nav.feature# Writes to features/reports/admin/index.html HEADED=1 npx y2k-guestbook test -p admin features/features/admin -
When Google's login screen appears, sign in with your allow-listed admin user.
For gated (unauthorized) scenarios, sign in with a Google account that is not an administrator.
Optional Prefixes
By default, Playwright hides the browser window. To watch a run:
# Headed runs will run in Chrome by default
HEADED=1 npx y2k-guestbook test
To hit a deployed URL instead of localhost:
BASE_URL=https://<your-deployed-url>.vercel.app npx y2k-guestbook test
Gherkin .feature Files
The packaged features/features/ directory contains the following .feature files displayed in this section.
Guestbook
Feature: Guestbook page navigation
Background:
Given a user is on the guestbook page
Scenario: Admin login link is on the guestbook page
When a user clicks the `admin login` link
Then they are directed to the Google SSO page for the admin dashboard
Feature: Guestbook page
Background:
Given a user is on the guestbook page
@submit-comments
Rule: Guestbook allows users to submit comments
Scenario: User submits a valid comment
When a user enters in a valid display name and comment body
And they click the `submit` button
Then their comment displays below the submission form as the topmost entry
Scenario: User submits an empty display name
When a user leaves the display name empty
And they click the `submit` button
Then the comment is not submitted
Scenario: User submits an empty comment body
When a user leaves the comment body empty
And they click the `submit` button
Then the comment is not submitted
Scenario: User submits a comment with an email
When a user submits a valid comment with a valid email address
Then their comment displays without their email
Scenario: User submits a comment with an invalid email
When a user submits a comment with an invalid email address
Then the comment is not submitted
Scenario: User submits a comment with English profanity
When a user enters a valid display name
And enters a comment body that contains English profanity
And they click the `submit` button
Then their comment is rejected and not submitted
And the user is shown an error message
# Default display name limit is 128 characters
Scenario: User submits a display name above the character limit
When a user submits a comment with a display name longer than 128 characters
Then their comment is rejected and not submitted
And the user is shown an error message
# Default email limit is 254 characters
Scenario: User submits an email above the character limit
When a user submits a comment with an email longer than 254 characters
Then their comment is rejected and not submitted
And the user is shown an error message
# Default comment body limit is 1000 characters; 10000 is the maximum allowed configuration
Scenario: User submits a comment body above the configured character limit
When a user submits a comment with a comment body longer than 1000 characters
Then their comment is rejected and not submitted
And the user is shown an error message
# Default rate limit is 1 comment every 5 minutes and 2 comments per 24 hours
Scenario: User submissions are rate limited
When a user submits more than 1 comment within 5 minutes
Then their comment is rejected and not submitted
And the user is shown an error message
@pagination
# Public page size comes from settings (default 10); seed only if count is below page size + 1
Rule: Guestbook entries are paginated
Background:
Given there are enough comments to trigger pagination
Scenario: User views the next page of comments
When a user clicks the `next` button for comment pagination
Then the next page of comments displays
Scenario: User views the previous page of comments
When a user clicks the `prev` button for comment pagination
Then the previous page of comments displays
Admin Dashboard
Feature: Unauthorized admin login
Background:
Given the user does not have admin authorization
And a user is on the guestbook page
Scenario: Unauthorized user is denied the dashboard
When a user clicks the `admin login` link
And they sign in successfully with Google
Then they are redirected to the guestbook page
And they are shown a message that they do not have admin authorization
Scenario: Unauthorized visitor message can be dismissed
Given the user is shown a message that they do not have admin authorization
When they click the `Close` button
Then the message closes
Feature: Authorized admin login
Background:
Given a user is on the guestbook page
And the user has admin authorization
Scenario: Authorized admin reaches the dashboard
When a user clicks the `admin login` link
And they sign in successfully with Google
Then they are redirected to the `/admin` dashboard
Feature: Admin Dashboard page navigation
@navigation
Rule: Navigation is a button
Background:
Given an authorized admin is signed in with Google SSO
And they are on the admin dashboard (`/admin`)
Scenario: View Guestbook link works
When an admin clicks the `view guestbook` link
Then they are taken to the guestbook page
@mobile-menu
Rule: Comment sort, filter, and search are in a collapsible menu on mobile
Background:
Given an authorized admin is signed in with Google SSO
And they are on the admin dashboard on mobile (`/admin`)
Scenario: Collapsible menu contains comment sort, filter, and search options
When an admin clicks the collapsible `comments menu` button
Then the `comments menu` opens showing comment sort, filter, and search options
Feature: Admin Dashboard comment interactions
Background:
Given an authorized admin is signed in with Google SSO
And they are on the admin dashboard (`/admin`)
@manage-comments
Rule: Admins can interact with comments
Scenario: Admins can filter comments by read status
When admins filter comments by read status
Then comments without the selected criteria are hidden
Scenario: Admins can filter comments by date range
When admins filter comments by date range
Then comments without the selected criteria are hidden
Scenario: Admins can filter comments by email presence
When admins filter comments by whether they were submitted with an email
Then comments without the selected criteria are hidden
Scenario: Admins can sort comments by newest
When admins sort comments by newest timestamps
Then the comments sort according to the selected criteria
And the sort display indicator updates accordingly
Scenario: Admins can sort comments by oldest
When admins sort comments by oldest timestamps
Then the comments sort according to the selected criteria
And the sort display indicator updates accordingly
Scenario: Admins can search comments
When admins search comments by keywords
Then comments not matching submitted keywords are hidden
Scenario: Filter, sort, and search results stack
Given admins have applied a sort, a filter, and a search to comments
Then results stack on top of other sorts, filters, or searches
Scenario: Admins can clear applied filters or sorting
Given admins have applied search keywords, sorting, or filters to comments
When they click the `clear all` link
Then search, filter, or sort results are cleared
Scenario: Admins can change a comment's read status
When admins mark a comment read or unread
Then the read status saves automatically
And the unread count updates accordingly
Scenario: Admins can mark all comments read
When admins click the `mark all read` button
Then all comments are marked read
And the unread count updates accordingly
Scenario: Admins can mark all comments unread
When admins click the `mark all unread` button
Then all comments are marked unread
And the unread count updates accordingly
Scenario: Admins can edit comments
When admins click on the `edit` button for a comment
And they edit the comment body
And they click away from the text editor
Then the changes are saved
Scenario: Admins can delete comments
When admins click on the `delete` button for a comment
And they click the `confirm` button
Then the comment is removed
Scenario: Admins can cancel deleting a comment
When admins click on the `delete` button for a comment
And they click the `cancel` button
Then the comment is not removed
@pagination
# Admin always shows 10 comments per page; seed only if fewer than 11 exist
Rule: Guestbook comments are paginated
Background:
Given there are enough comments to trigger pagination
Scenario: Admin views the next page of comments
When an admin clicks the `next` link for comment pagination
Then the next page of comments displays
Scenario: Admin views the previous page of comments
When an admin clicks the `prev` link for comment pagination
Then the previous page of comments displays
Feature: Admin Dashboard settings
Rule: Admin Dashboard settings apply changes
Background:
Given an authorized admin is signed in with Google SSO
And they are on the admin dashboard (`/admin`)
Scenario: Admin can update the guestbook title
When an admin updates the `title` setting
And they click the `save changes` button
Then those changes are applied to the admin dashboard
And those changes are applied to the guestbook page
Scenario: Admin can undo changes to guestbook settings
When an admin updates the `title` setting
And they click the `undo changes` button
Then those changes are not applied to the admin dashboard
And the settings revert to the previously entered values
Scenario: Admin can update the comment placeholder
When an admin updates the `placeholder` setting
And they click the `save changes` button
Then those changes are applied to the guestbook page
Scenario: Admin can turn off the title marquee
When an admin sets the `marquee` setting to `off`
And they click the `save changes` button
Then the guestbook title displays without a marquee
Scenario: Admin can set the main font and size
When an admin updates the `main font` and its `font size`
And they click the `save changes` button
Then those changes are applied to the guestbook page
Scenario: Admin can set the accent font and size
When an admin updates the `accent font` and its `font size`
And they click the `save changes` button
Then those changes are applied to the guestbook page
Scenario: Admin can set comments per page
When an admin updates the `comments per page` setting
And they click the `save changes` button
Then the guestbook page paginates at that page size
Scenario: Admin can turn off email capture
When an admin sets the `capture email` setting to `off`
And they click the `save changes` button
Then the email field is not shown on the guestbook page
Scenario: Admin can set a custom comment length
When an admin adjusts the `comment length` setting to an allowed limit
And they click the `save changes` button
Then those changes are applied to the guestbook page
Scenario: Admin can set custom rate limits
When an admin adjusts the `rate limits` settings to allowed limits
And they click the `save changes` button
Then those changes are applied to the guestbook page
Scenario: Admin can allow-list profanity
When an admin enters comma-separated values in the `profanity allow-list`
And they click the `save changes` button
Then those changes are applied to the guestbook page
Scenario: Admin can view custom CSS example file
When an admin clicks on the `view example` link for the `custom theme` setting
Then a new tab or window opens to the custom CSS example file
Write Additional Tests
If a Given / When / Then already has a matching function, Playwright runs that function. A line with no match fails with Missing step: and the text of that line. Create features/step_definitions/your-steps.ts for that line, and import the testing functions:
import { Given, When, Then } from "y2k-guestbook/testing";
Given("your scenario here", async function () {
await this.page.goto(this.guestbookUrl());
});
Fill in the body with the appropriate code to execute the step. Instruction should be written the way a human user would find things on the page — for example, reference the submit button, the display name field, the prev / next links, and not CSS class names or code.
- It doesn't matter what the file is called, as long as it's in the
features/step_definitions/directory and has the.tsextension. - Stash values you will need later on the World object (for example
this.postedName).