ClassAct is a lightweight library that wraps Playwright to enhance automated testing and offer extended capabilities. ClassAct allows you to leverage the power of Playwright with less code.
- Extended Page and Element Capabilities: Adds additional functionality to the Playwright page and element locators.
- Page Object Model (POM): Provides a clean and DRY Page Object Model (POM) framework.
- Mocking: Adds ability to easily mock API interactions, current user, and current date/time, allowing tests to be deterministic and fast.
npm install class-act --save-dev
Call setClassActConfig()
to configure ClassAct. Since this method call must occur prior to any test execution, a good place to call this is in playwright.config.ts
.
import { setClassActConfig } from 'class-act';
...
setClassActConfig({
siteUrl: "http://localhost:3000",
apiUrl: "http://localhost:5000",
testIdAttribute: "test-id",
mockFileDirectory: "e2e/mocks/files",
storagePath: "state.json",
launchSettings: {
headless: false,
devtools: false,
slowMo: 2000,
},
});
ClassAct allows you to create page objects with ease.
For creating your page objects, there are two objects you will need. These classes serve as wrappers to Playwright page and element locators.
-
Page
represents any routeable view in your application. This should be the base class for any pages you create. -
PageElement
represents any HTML element. You can cast elements on your page directly to class, or extent your element by creating your own element class that inherits from PageElement.
When defining the elements within the structure of your page, use one of the following methods:
- find(selector, type)
- findAll(selector, type)
Your selector (string) can be any standard CSS selector. You can also use the @ symbol to locate your element using the test-id attribute, as configured when you call setClassActConfig().
The type passed to find or findAll must be an class that inherits from PageElement
.
-
Define the page object under test. Be sure to inherit from
Page
, or a base class inheriting frompage
.import { Page, PageElement, PageFormElement } from 'class-act'; ... export class SearchPage extends Page { path = "search"; get form() { return this.find("form", SearchForm); } get results() { return this.findAll("#results>div", SearchResult); } }
-
Define complex elements located on the page.
export class SearchForm extends PageElement { get keywordField() { return this.find("input", PageFormElement); } get searchButton() { return this.find("button", PageElement); } } ...
- Initialize the App:
Optionally, you can provide mocks for the API interactions, browser storage, and/or dateTime during initialization.
import { App } from 'class-act'; ... const app = await App.init();
const app = await App.init({ sessionStorage: [ { 'accessToken': '12195f53-6ce3-4b5c-907d-6fe8408d53d5' } ], api: apiDefinitions, dateTime: new Date(2024, 1, 1) });
- Navigate to a page:
const searchPage = await app.navigate(SearchPage);
- Interact with elements using page objects.
await searchPage.form.keywordField.setValue('Test'); await searchPage.form.searchButton.click(); expect(await searchPage.loadingIndicator.isVisible);
Creating definitions for API endpoints serves two primary purposes:
- Data mocking
- Pausing test exection until an one or more API calls have completed
Setup definition for each API endpoint:
import { ApiMethod, ApiRequest } from "../class-act";
export class GetPeopleSearch extends ApiRequest {
method: ApiMethod = "GET";
path = "search";
constructor(keyword: string) {
super({ queryParams: { keyword } });
}
}
Be sure to properly map parameters to routeParams, queryParams, or body.
export const testData = [
mock(
new api.GetUserById("user@test.com"),
new SuccessResponse({ name: "Test User", dashboards: [], theme: "light" }),
),
mock(
new api.GetUserById("john@test.com"),
new SuccessResponse({ name: "John Tester", dashboards: [], theme: "dark" }),
),
...
];
mock(
new api.GetSearchResults("test"),
new SuccessResponse(fromFile("searchResults.json")),
)
mock(
new api.GetSearchResults("fail"),
new FailureResponse(404)
)
The ClassActConfig
interface defines the configuration settings used across various components of the application, particularly those interacting with the web and testing environments.
-
siteUrl:
string
- The base URL of the site under test. -
apiUrl:
string
- The base URL for API calls. -
testIdAttribute:
string
- The attribute name used to identify elements during testing (e.g., data-testid). -
mockFileDirectory:
string
- Directory path where mock files for testing are stored. -
storageStateDirectory:
string
- Directory path where state files are stored. -
launchSettings:
LaunchOptions
- Configuration options for launching the browser or other test environments.
The App
class serves as a Page Object wrapper for Playwright's Browser and Context objects, providing a structured way to interact with a web application's browser environment. It includes methods for navigation, mocking data, and handling API calls. Instances of this class should be created using the static init
method.
- The constructor is private to enforce the use of the
init
method for creating instances.
-
config (protected):
ClassActConfig
- Configuration settings for the class instance. -
browser (protected):
playwright.Browser | undefined
- Holds the Playwright Browser instance, if initialized. -
context (protected):
playwright.BrowserContext | undefined
- Holds the Playwright BrowserContext instance, if initialized.
-
mockApi(apiMocks: ApiMock[]): void - Mocks API endpoints.
-
mockDateTime(dateTime: Date): void - Mocks the current date/time.
-
setLocale(locale: string): void - Sets the browser's default locale.
-
setCookies(cookies: Cookie[]): void - Adds cookies to the browser context.
-
setLocalStorage(items: StorageItem[]): void - Sets localStorage values on the browser.
-
setSessionStorage(items: StorageItem[]): void - Sets sessionStorage values on the browser.
-
currentPage<T extends Page>(type: new (page: playwright.Page) => T): Promise - Returns the page currently loaded in the context.
-
goBack<T extends Page>(type: new (page: playwright.Page) => T): Promise - Navigates backward in the browser history.
-
goForward<T extends Page>(type: new (page: playwright.Page) => T): Promise - Navigates forward in the browser history.
-
navigate<T extends Page>(type: new (page: playwright.Page) => T, params?: { route?: any; query?: any; hash?: string; }): Promise - Navigates to the provided page type, with optional route and query parameters.
- init(options?: AppOptions): Promise - Initializes the application with optional configurations.
const app = await App.init({
apiMocks: [
new ApiMock("/api/data", { data: [1, 2, 3] })
],
dateTime: new Date("2023-01-01")
});
const currentPage = await app.currentPage(HomePage);
await app.mockUser({ name: "Jane Doe" });
await app.navigate(SettingsPage);
AppOptions
allows the app to be configured when calling App.init()
.
-
apiMocks:
ApiMock[]
- Sets substitutions for API interactions. -
dateTime:
dateTime
- Sets the value of Date.now(). -
locale:
string
- Sets the browsers language property. -
cookies:
Cookie[]
- Sets cookies. -
localStorage:
StorageItem[]
- Sets localStorage. -
sessionStorage:
StorageItem[]
- Sets sessionStorage.
-
name:
string
- Unique identifier within domain and path. -
value:
string
- Data stored in the cookie. -
domain:
string
- Domain the cookie is valid for (defaults to creation domain). -
path:
string
- Path on server the cookie is valid for (defaults to current page's path). -
expires:
number
- Expiration time in seconds since Unix epoch (defaults to session cookie). -
httpOnly:
boolean
- Only accessible by server, not client-side scripts (defaults to false). -
secure:
boolean
- Only transmitted over secure connections (defaults to false). -
sameSite:
"Strict" | "Lax" | "None"
- Controls cookie behavior in cross-site requests:
-
key:
string
- Key to use for storage item. -
value:
string
- Serialized value of storage item.
The Page
class serves as an abstract base class for specific page objects within a web application managed by Playwright. It provides common functionalities like navigation, fetching page details, and handling page elements. Instances are generally created through navigation methods in the App
class.
- The constructor takes a Playwright Page object and initializes the page properties. The constructor should not be used directly. Use the
navigate()
and method ofApp
instead.
-
page (protected):
playwright.Page
- Reference to the Playwright Page object. -
path (abstract):
string
- The specific path or route associated with the page. -
config (protected):
ClassActConfig
- Global ClassAct configuration.
-
title:
Promise<string>
- Returns the title of the current page. -
url:
URL
- Returns the URL of the current page. -
params:
{ [k: string]: string }
- Retrieves URL parameters as an object. -
viewportSize:
{ width: number; height: number } | null
- Returns the current viewport size of the page. -
body:
PageElement
- Returns the mainPageElement
object representing the body of the page. -
close():
Promise<void>
- Closes the current page. -
screenshot(options?: playwright.PageScreenshotOptions):
Promise<Buffer>
- Takes a screenshot of the current page. -
setViewportSize(width: number, height: number):
Promise<void>
- Sets the viewport size of the page. -
load(params?: { route?: any; query?: any; hash?: string; }):
Promise<void>
- Loads or reloads the page with optional parameters. -
waitForUrl(url: string | RegExp | ((url: URL) => boolean), options?: {
timeout?: number | undefined;
waitUntil?: "load" | "domcontentloaded" | "networkidle" | "commit" | undefined;
} | undefined):
Promise<void>
Waits for the main frame to navigate to the given URL. -
waitForApiResponse(apiRequest: ApiRequest, timeout?: number):
Promise<void>
- Waits for a response to a specified API request. -
waitForAllApiResponses(apiRequests: ApiRequest[], timeout?: number):
Promise<void[]>
- Waits for all responses to specified API requests. -
executeScript<T>(script: Function, ...var_args: any[]):
Promise<T>
- Executes a script on the page and returns a result. -
find<T extends PageElement>(selector: string, type: new (locator: playwright.Locator) => T):
T
- Finds the first element matching the selector and casts it to the specified type. - findAll<T extends PageElement>(selector: string, type: new (locator: playwright.Locator) => T): `PageElementCollection
class HomePage extends Page {
get path() {
return "/home";
}
async navigateToSettings() {
return this.navigate(SettingsPage);
}
}
const homePage = await app.currentPage(HomePage);
const title = await homePage.title;
const settingsPage = await homePage.navigateToSettings();
The PageElement
class wraps Playwright's Locator object, providing an API for interacting with specific elements on a web page. It encapsulates common DOM operations and ensures elements are accessed in a consistent manner across different page objects.
- Accepts a Playwright Locator object and initializes the element properties. The constructor should not be used directly. Use the
find()
method ofPage
andPageElement
instead.
-
locator (protected):
playwright.Locator
- The Playwright Locator object associated with the element. -
config (protected):
ClassActConfig
- Global ClassAct configuration.
-
selector:
string
- Returns the selector used to locate the element. -
html:
Promise<string>
- Fetches the inner HTML of the element. -
tagName:
Promise<string>
- Returns the tag name of the element. -
id:
Promise<string | null>
- Retrieves the ID attribute of the element. -
title:
Promise<string | null>
- Fetches the title attribute of the element. -
testId:
Promise<string | null>
- Gets a custom attribute typically used for identifying elements in tests. -
classList:
Promise<DOMTokenList>
- Returns the list of classes applied to the element. -
height:
Promise<number>
- Returns the height of the element. -
width:
Promise<number>
- Returns the width of the element. -
text:
Promise<string>
- Fetches the visible text contained within the element. -
isVisible:
Promise<boolean>
- Checks if the element is visible on the page. -
isHidden:
Promise<boolean>
- Checks if the element is hidden on the page. -
isEnabled:
Promise<boolean>
- Determines if the element is enabled. -
isDisabled:
Promise<boolean>
- Checks if the element is disabled. -
isInView:
Promise<boolean>
- Checks if the element is within the visible viewport. -
click():
Promise<void>
- Simulates a mouse click on the element. -
dblclick():
Promise<void>
- Simulates a double-click on the element. -
dragTo(element: PageElement):
Promise<void>
- Drags the current element to another element. -
scrollTo():
Promise<void>
- Scrolls the page to the element. -
scrollIntoView():
Promise<void>
- Scrolls the element into the visible area of the browser window. -
blur():
Promise<void>
- Removes focus from the element. -
focus():
Promise<void>
- Sets focus on the element. -
getAttribute(name: string):
Promise<string | null>
- Retrieves a specified attribute from the element. -
getComputedStyle(property: string):
Promise<string>
- Gets the computed style of the element. -
waitForLoad(timeout?: number):
Promise<void>
- Waits until the element is attached to the DOM. -
waitForUnload(timeout?: number):
Promise<void>
- Waits until the element is detached from the DOM. -
waitForVisible(timeout?: number):
Promise<void>
- Waits for the element to become visible. -
waitForHidden(timeout?: number):
Promise<void>
- Waits for the element to become hidden. -
find<T extends PageElement>(selector: string, type: new (locator: playwright.Locator) => T):
T
- Finds the first sub-element that matches the selector. -
findAll<T extends PageElement>(selector: string, type: new (locator: playwright.Locator) => T):
PageElementCollection<T>
- Finds all sub-elements matching the selector.
const loginButton = this.find('#login-button', PageElement);
await loginButton.click();
The PageElementCollection
class is designed to manage collections of PageElement
objects, providing utilities to interact with multiple elements simultaneously. It facilitates operations on groups of elements found through specific selectors using Playwright's Locator.
- The constructor takes a Playwright Locator object and a constructor function for the
PageElement
type, initializing the collection properties. The constructor should not be used directly. Use thefindAll()
method ofPage
andPageElement
instead.
-
locator (protected):
playwright.Locator
- The Playwright Locator used to define the collection of elements. -
type (protected):
new (locator: playwright.Locator) => T
- The constructor function of thePageElement
type for elements in the collection.
-
selector:
string
- Returns the selector used for locating the elements in the collection. -
items:
Promise<T[]>
- Retrieves all elements in the collection as an array ofPageElement
objects. -
count:
Promise<number>
- Returns the number of elements in the collection. -
first:
T
- Returns the first element in the collection. -
last:
T
- Returns the last element in the collection. -
nth(index: number):
T
- Retrieves the nth element from the collection. -
findByText(text: string | RegExp, exact?: boolean):
T
- Finds an element by its text content, with an option for exact match. -
findByTitle(text: string | RegExp, exact?: boolean):
T
- Finds an element by its title attribute, with an option for exact match. -
find(predicate: (value: T, index: number, obj: T[]) => boolean | Promise):
Promise<T | undefined>
- Finds an element based on a custom predicate function. -
filter(predicate: (value: T, index: number, array: T[]) => boolean | Promise):
Promise<T[]>
- Filters elements in the collection based on a custom predicate function. -
every(predicate: (value: T, index: number, array: T[]) => boolean | Promise):
Promise<boolean>
- Checks if every element in the collection satisfies the predicate. -
some(predicate: (value: T, index: number, array: T[]) => boolean | Promise):
Promise<boolean>
- Checks if some elements in the collection satisfy the predicate. -
map(callbackfn: (value: T, index: number, array: T[]) => V | Promise):
Promise<V[]>
- Creates a new array with the results of calling a provided function on every element in the calling array. -
forEach(callbackfn: (value: T, index: number, array: T[]) => void | Promise):
Promise<void>
- Executes a provided function once for each element in the collection. -
waitForLoad(timeout?: number):
Promise<void>
- Waits for all elements in the collection to be loaded in the DOM. -
waitForUnload(timeout?: number):
Promise<void>
- Waits for all elements in the collection to be unloaded from the DOM.
const buttons = new PageElementCollection(page.locator('button'), ButtonElement);
await buttons.forEach(async (button) => await button.click());
const areAllButtonsVisible = await buttons.every(button => button.isVisible);
PageFormElement
extends PageElement
to provide specific functionalities for interacting with form elements on a web page, such as input, textarea and select fields.
-
type:
Promise<string | null>
- Retrieves the type of the form element (e.g., text, checkbox). -
value:
Promise<string>
- Gets the current value of the form element. -
isChecked:
Promise<boolean>
- Determines whether a checkbox or radio button is checked. -
isValid:
undefined
- Currently, this property is not implemented and returns undefined.
-
clear():
Promise<void>
- Clears the value of the form element. -
check():
Promise<void>
- Checks a checkbox or radio button. -
uncheck():
Promise<void>
- Unchecks a checkbox or radio button. -
setValue(value: string):
Promise<void>
- Sets the value of the form element. -
appendValue(value: string):
Promise<void>
- Appends additional text to the current value of the form element.
PageHyperLink
extends PageElement
specifically for hyperlink elements, providing access to hyperlink-specific attributes.
-
href:
Promise<string | null>
- Retrieves the URL to which the hyperlink points. -
target:
Promise<string | null>
- Gets the target frame or window in which the linked document will open.
PageImageElement
extends PageElement
to cater specifically to image elements, allowing access to image-specific attributes.
-
src:
Promise<string | null>
- Retrieves the source URL of the image. -
altText:
Promise<string | null>
- Gets the alternative text of the image, used for accessibility and when the image cannot be displayed.
ApiRequest
is an abstract base class for defining API request details, including the method, path, and input parameters.
- Accepts optional
ApiRequestInput
to initialize request inputs.
-
inputs:
ApiRequestInput
- Contains input parameters like route parameters, query parameters, and data. -
method (abstract):
ApiMethod
- Specifies the HTTP method of the request (GET, POST, PUT, DELETE). -
path (abstract):
string
- Specifies the endpoint path. -
config (protected):
ClassActConfig
- Global ClassAct configuration.
-
url:
string
- Constructs and returns the full URL for the request based on the path and inputs.
The ApiRequestInput
interface provides structure for input data used in API requests, including route parameters, query parameters, and body data.
-
routeParams:
any
- Parameters embedded in the route path. -
queryParams:
any
- Parameters appended to the URL as query strings. -
data:
any
- Data sent in the body of the request, applicable for methods like POST and PUT.
ApiMethod
is a type defining the allowed HTTP methods for API requests. Accepted values are:
- "GET"
- "POST"
- "PUT"
- "DELETE"
ApiResponse
is an abstract base class for defining the structure of API responses.
-
status (abstract):
number | undefined
- The HTTP status code of the response. -
message (abstract):
string | undefined
- A message accompanying the status code. -
data (abstract):
any
- The payload returned in the response.
SuccessResponse
represents a successful API response structure, extending ApiResponse
.
- Accepts optional
any
data to initialize the response data.
-
data:
any
- Data returned in the response. -
status:
number | undefined
- The HTTP status code, typically in the 200 range. -
message:
string | undefined
- Optional message describing the success.
FailureResponse
represents an error or failure in API response, extending ApiResponse
.
- Requires
status: number
and accepts an optionalmessage
.
-
status:
number
- The HTTP status code, typically in the 400 or 500 range. -
message:
string | undefined
- Optional message detailing the error. -
data:
any
- Additional data related to the error.
The ApiMock
interface is used to simulate API requests and responses, ideal for testing scenarios.
-
request:
ApiRequest
- Defines the API request details. -
response:
ApiResponse
- Defines the expected response for the mock.