End-to-end tests
Introduction to end-to-end tests
End-to-end (E2E) testing helps you verify the complete flow of an application from the perspective of your users. It complements other quality measures such as branching and merging, code reviews, unit tests, smoke tests, and validations by providing confidence that the system works as intended in real-world scenarios. Where unit and integration tests focus on smaller components, end-to-end tests confirm that these components work together correctly.
Testwise for end-to-end testing
Applications built with the Thinkwise Platform are often complex and remain in use for many years. To ensure quality, test automation is essential. It provides consistent and repeatable checks and reduces the need for manual regression testing, so testers can focus on new features and critical areas. We recommend to automate tests for stable, user-accepted functionality and perform the tests with test accounts that represent actual Role & User Group rights.
Testwise is a test automation solution designed specifically for Thinkwise applications in the Universal UI. Built on Playwright, it adds prebuilt page extensions, custom controls, and components that follow Thinkwise UI patterns. With these, you can easily interact with complex screens such as grids, forms, and action bars. Testwise also includes scripts for authentication and user simulation, helping you avoid repetitive setup tasks and improving test stability.
In summary, Testwise streamlines end-to-end testing by turning common Thinkwise actions into clear, reusable methods. This allows development teams to focus on validating business logic.
Testwise is designed for applications using the Universal UI. If you have applications that use a different UI, such as the Windows GUI, we suggest migrating to the Universal UI to benefit from Testwise and other modern features. For more information, see Transition to the Universal UI.
Benefits of Testwise
Using Testwise as part of your Software Factory application testing and automation efforts offers several benefits:
- You do not have to maintain components yourself. If the Universal UI changes, Thinkwise maintains and update component logic in Testwise.
- Test development is faster with built-in, ready to use functions and helpers that you can use directly.
- The Thinkwise Application model is synced to your Testwise project and for all
Subject <> Screen Typecombinations Subject page objects are automatically created in the Testwise repository.
One limitation of Testwise is the learning curve. This is because Testwise is a technology-specific automation library, so it takes time and effort to become familiar with its features.
Set up Testwise
Prerequisites
- Install Visual Studio Code (Windows, macOS, or Linux).
- Open Visual Studio Code (VSCode) and go to extensions.
- Search Playwright Test for VSCode and install the extension.
- Install Node.js.
- Create a folder in your file explorer and open it. This folder will be the folder for your test project.
- Press Alt + D to select the address bar.
- Type
cmdand press Enter to open the terminal. - In the terminal type
code .and press Enter to open VSCode. - In VSCode press Ctrl+~ to open up the terminal within VSCode.
Set up project dependencies
-
To create a new NPM project run the following command:
npm init- Press Enter until you see the following line in the terminal:
type: (commonjs). - Enter
moduleand press Enter. - A confirmation message will pop-up, press Enter again.
- Press Enter until you see the following line in the terminal:
-
To install Testwise, run:
npm i @thinkwise/testwise -
Open the
testwise.jsonfile. -
Replace the example values inside
environmentSettingswith valid details:{
"environmentSettings": {
"baseUrl": "https://<your-environment>",
"serviceUrl": "https://<your-environment>/indicium",
"metaEndpoint": "iam",
"authUser": "<username>",
"authUserPassword": "<password>",
"guiApplAlias": "<application-alias>"
}
}note- Use a test user instead of personal login details.
- The baseUrl should match your Universal login page.
- The serviceUrl should match the Meta server URL up until
indicium.
Initialize Playwright
To initialize Playwright, run:
npm init playwright
- The message 'Where to put your end-to-end tests?' is shown on the screen. To continue, press Enter.
- The message 'Add a GitHub Actions workflow? is shown on the screen. To continue, press N to select No.
- The message 'Install Playwright browsers (can be done manually via 'npx playwright install'? is shown on the screen. To continue, press Enter.
- Open the file
playwright.config.ts, then change// baseURL: 'http://localhost:3000',tobaseURL: `${testwiseConfig().get<string>('environmentSettings.baseUrl')}`, - Select the
testWiseConfigpart of the baseURL and press Ctrl+.. - Select
Add import from "@thinkwise/testwise"as import option. The import will be added at the top of your code. - Save your changes, by pressing Ctrl+S.
Sync the application model to Testwise
Synchronizing the application model to Testwise will generate Subject page objects. You can directly import and use these in your tests or extend in your own pages, in addition to all Thinkwise components.
To sync the application model, run:
npx testwise sync
Repeat this step whenever the Thinkwise Application model is updated on the test environment.
The following phases take place:
Phase 1: Cleanup and Initial Compilation
Phase 2: Build Model Data
Phase 3: Refine Model Data
Phase 4: Generate Artifacts
Phase 5: Final Compilation
Phase 6: Backup
The end of the sync is clearly indicated:
Sync process completed successfully!
In model synchronization:
- Page objects will be generated for the subjects the authUser is authorized for in IAM. This can take a couple of minutes depending on your application size.
- If your
testwise.jsonconfiguration is already in place before installing this version, the model and artifacts are generated automatically during installation. - If you install first and add/update configuration afterwards, you will need to run the sync command above to retrieve all updates.
Introducing Generated Artifacts
The sync step resulted in auto-generated page objects in Testwise. You can directly import and use these page objects in your tests or extend in your own pages. The page objects are essentially a combination of a Subject and Screen type as configured in the Software Factory. Use these to ensure your test scripts are simpler, shorter, and less maintenance-prone in the future. You can find an example below to help you get started.
Naming convention
There are several naming conventions:
- Subject name is converted from snake case to Pascal case
- Screen type is one of:
- Main
- Detail
- Popup
- Zoom
Examples:
| Model subject | Variant | Screen | Generated name |
|---|---|---|---|
category | - | Main | CategoryMain |
category | - | Detail | CategoryDetail |
category | Admin | Main | CategoryAdminMain |
Create your first test
To create a linear test script from a record with playback functionality in Playwright:
- In VSCode, go to the tab Testing and select Record new. This open a new browser to start recording your actions.
- Go to your application.
- Log in and perform the actions for your test case.
- Close the browser to stop the recording.
Example: linear script with record and playback function
import { test, expect } from '@playwright/test';
test.describe('Sample Test Suite', () => {
test('Add time entry', async ({ page }) => {
await page.goto('https://tcp.thinkwise.app/universal/#application=tcp');
await page.getByTestId('login__username__input').click();
await page.getByTestId('login__username__input').fill('testUser');
await page.getByTestId('login__password__input').click();
await page.getByTestId('login__password__input').fill('Xyz@245');
await page.getByTestId('login__login-button').click();
await page.getByTestId('listbar__project-beheer').click();
await page.getByTestId('listbar__project-beheer__item__0').click();
await page.getByTestId('actionbar__add').click();
await page.getByTestId('form-field__project-id__control__input').click();
await page.getByTestId('form-field__project-id__control__input').fill('q');
await page.getByTestId('form-field__project-id__control__option__quality-assurance').click();
await page.getByTestId('form-field__sub-project-id__control__input').click();
await page.getByTestId('form-field__sub-project-id__control__option__automated-testing').click();
await page.getByTestId('form-field__aantal-uren__input').click();
await page.getByTestId('form-field__aantal-uren__input').fill('1');
await page.getByTestId('form-field__activiteit-id__control__input').click();
await page.getByTestId('form-field__activiteit-id__control__input').fill('Build example test');
await page.getByTestId('form-field__omschrijving__input').click();
await page.getByTestId('form-field__omschrijving__input').fill('This is my description');
await page.getByTestId('form-field__adres-id__control__input').click();
await page.getByTestId('form-field__adres-id__control__input').fill('Test Street');
await page.getByTestId('actionbar__save').click();
expect(await page.getByTestId('screen__uren-boeken-dag__grid1').getByRole('row').all).toEqual(1);
});
});
Convert the script with Testwise
Apply common test automation patterns in the optimized script. For example, the AAA pattern and Page Object Model (POM) pattern. This approach maximizes reusability and minimizes maintenance by reducing repeating logic.
Testwise has functionality to replace and convert steps in the recording. To be able to use this functionality you need to import the test fixture from Testwise.
To import the test fixture, follow these steps:
-
Change the import on line 1 to:
import { expect } from '@playwright/test'; -
Add a new import:
import { test } from '@thinkwise/testwise'; -
Remove all steps part of the login process where the test id starts with login and replace with:
await page.logInWithCredentials('username', 'password'); -
In Testwise, screens are exposed as typed subjects on
page.subject. Introduce the subject once:const event = await page.subject.EventMain; -
Replace
page.getByTestIdwith the Subject structure:Playwright (old) Testwise (new) page.getByTestId('actionbar__add')event.toolbarXXXX.getAddButton()page.getByTestId('form-field__x__input')event.form1.getFieldByColId('x')lookup option click event.form1.<lookup_name>.lookupSelect('value')grid selector event.grid1 -
Refactor to act as components, not selectors:
Instead of:
await page.getByTestId('form-field__event-name__input').fill('1');You always convert to:
await event.form1.getFieldByColId('event-name').fill('1'); -
Go back to the Testing tab in VSCode.
-
Run your test again by selecting the test case and selecting Run test. If your test passes, you are successfully linked to the Testwise library.
Example: optimized script
import { expect } from '@playwright/test';
import { test } from '@thinkwise/testwise';
test.describe('Sample Test Suite', () => {
test('Add time entry', async ({ page }) => {
//Arrange
const event = await page.subject.EventMain;
await page.logInWithCredentials('testUser', 'Xyz@245');
await page.goToDeepLink('subject=event');
// Act
await event.toolbar503DB1BD.getAddButton().click();
await event.form1.getFieldByColId('event-name').fill('A random event');
await event.form1.getFieldByColId('event-date').fill('10/10/2027');
await event.form1.categoryIdDropdown.lookupSelect('Internal');
await event.form1.LocationtIdDropdown.lookupSelect('DE');
await event.toolbar503DB1BD.getSaveButton().click();
// Assert
await expect(event.grid2.verifyNumberOfRecordsInGrid(1)).resolves.not.toThrow();
});
});
Scripts
To log in via the terminal and run a test:
npx testwise-credentials <script_name_to_run_tests>
An example of test scripts for the testwise-credentials command:
"scripts": {
"test": "npx playwright test --ui --reporter=html --output=./playwright-report",
"debug": "npx playwright test --debug --reporter=html --output=./playwright-report",
"headless": "npx playwright test --reporter=html --output=./playwright-report"
}
Page extensions for Playwright
In this section, you can find commonly used functionalities that are built into the Playwright page object.
Deep linking
Deep linking allows you to bypass UI navigation.
The goToDeepLink() navigates within the current application, utilizing the configured baseUrl, to allow environment switching without tests breaking.
Examples how to use this functionality are provided.
-
To navigate to:
https://tcp.thinkwise.app/universal/#application=tcp/subject=tms_ticketawait page.goToDeepLink('subject=tms_ticket'); -
To navigate to:
https://tcp.thinkwise.app/universal/#application=tcp/subject=vw_persoon_capaciteit/subjectVariant=vw_persoon_capaciteit_medewerker_planningawait page.goToDeepLink('subject=vw_persoon_capaciteit/subjectVariant=vw_persoon_capaciteit_medewerker_planning');
The deep-link format for the Universal GUI is: https://[server]/#application=[alias]/processflow=[id]?....
The helper behind the goToDeepLink() method works with this format and automatically prefixes the configured baseURL, making tests portable across environments.
For more details on the URL structure, see Create a deep link for Universal GUI.
Authentication
The login method uses environment variables. The following script sets process environment variables to use with this method.
When you run the testwise-credentials script, executed tests use the specified credentials provided at runtime.
This uses the specified credentials on any tests where the await page.logIn() extension method is invoked.
-
logInWithCredentials()- To log in using test credentials, a username and password.import { test } from '@thinkwise/testwise';
test.describe('Log in with credentials', () => {
test.beforeEach(async ({ page }) => {
await page.logInWithCredentials('MyUsername', 'MyPassword');
});
test('should display the main menu after login', async ({ page }) => {
// Add your test steps here
});
}); -
logInWithOptions()- Used to pass additional information, such as a different application to launch into. See Enumerations for more options.import { test, type UniversalLoginOptions } from '@thinkwise/testwise';
test.describe('Log in with options', () => {
test.beforeEach(async ({ page }) => {
const options: UniversalLoginOptions = {
username: 'MyUsername',
password: 'MyPassword',
config: {
defaultApplication: 'iam'
}
};
await page.logInWithOptions(options);
});
test('should display the main menu after login', async ({ page }) => {
// Add your test steps here
});
}); -
logOut()- Used to log out in scenarios where logging out is required.import { test } from '@thinkwise/testwise';
test.describe('Test logout functionality', () => {
test.beforeEach(async ({ page }) => {
await page.logInWithCredentials('MyUsername', 'MyPassword');
});
test('should log me out successfully', async ({ page }) => {
await page.logOut();
// Add your verification steps here
});
});
User simulation
You need to be logged in as a user with simulation rights for this functionality to work. See User simulation for more information.
There are multiple ways to start or stop a user simulation:
-
simulateUser()- To simulate other users after logging in.test('should be able to simulate a different user and do stuff', async ({ page }) => {
await page.simulateUser('UsernameOfSimulatedUser');
// Do something as that user
}); -
getSimulatedUser()- Get the username of the currently simulated user.test('should be able to get simulated users username', async ({ page }) => {
const simulatedUser = await page.getSimulatedUser();
console.log(`Simulated user is: ${simulatedUser}`);
}); -
stopSimulation()- Stop user simulation.test('should be able to do other stuff after stopping user simulation', async ({ page }) => {
await page.simulateUser('UsernameOfSimulatedUser');
// Do something as simulated user
await page.stopSimulation();
// Do some other stuff as logged in user
});
Components
Components are the centralized logic that serve as counterparts to components in the Software Factory. They provide access to specific actions that make end-user interaction easier.
In Testwise there are mainly two types of components:
Screen Components- A collection of components defined in yourScreen Typeswithin Software Factory.Global Components- Generally static components such theTop-bar, or floating components such asPop-ups; basically anything that is not a screen component
For more information on the available screen components, see Universal screen components and Screen components.
Screen Components
Tab
Tabs represent tab strips in Thinkwise screens (for example, List tabs, Forms or Detail tabs). They allow navigation between sections within a subject or screen type.
In model-generated tests, the Component Tabs container on a subject is exposed on the tab as tab.
Hierarchy Generated component tabs follow the subject structure:
<subject> → <tab> → <tabaction>
Available methods for the Component tab:
- Open the List tab (defaults to tabindex = -1 for master):
openListTab(tabindex?: string): Promise<void>
openListTab(tabindex?: string): Promise<void>
- Get the locator for the List tab:
getListTab(tabindex?: string): Locator
await event.tab.getListTab().click();
- Open the Form tab:
openFormTab(): Promise<void>
await event.tab.openFormTab();
- Get the locator for the Form tab:
getFormTab(): Locator
await event.tab.getFormTab().click();
- Open a tab by its id (data-testid suffix):
openTabById(tabId: string): Promise<void>
await componentTab.openById('users');
- Open a tab by index:
await componentTab.openByIndex(2);
- Get the badge count for a tab:
await event.tab.openByIndex(0);
const badge = await event.tab.getBadgeCount('user-group-tags');
Returns 0 if no badge is visible.
Action bar
In model-generated tests, the Action Bar is automatically available through the generated subject structure. It is exposed as part of the tab hierarchy and provides strongly typed access to all standard Thinkwise action bar buttons.
No manual initialization or context configuration are required.
Usage
The action bar is accessed through the generated subject model:
await <yoursubjecthere>.<yourToolbarIdhere>.getUpdateButton().click();
Example
const event = await page.subject.EventMain;
await event.toolbar3574C040.getUpdateButton().click();
Hierarchy
Generated action bars follow the subject structure:
<subject> → <toolbar>
Where:
-
<subject>represents the generated subject object -
<toolbar>is the generated toolbar identifierEach toolbar exposes the standard Thinkwise action bar methods.
Accessing Buttons
Buttons are retrieved using dedicated getter methods. You can perform any Playwright action on the returned element. For buttons in the overflow menu, you need to include .overflowMenu to call them.
Example
await event.toolbar3574C040.getAddButton().click();
await event.toolbar3574C040.getDeleteButton().click();
await event.toolbar3574C040.getRefreshButton().click();
await event.toolbar3574C040.overflowMenu.getExportButton().click();
Available Methods
The generated Action Bar provides access to all standard buttons, including:
getAddButtongetCancelButtongetCopyButtongetDeleteButtongetRefreshButtongetSaveButtongetUpdateButtongetExportButtongetImportButtongetExportImmediatelyButtongetMassUpdateButtongetQuickFilterButtongetFilterButtongetClearAllFiltersButtongetRestoreSortOrderButtongetSortButtongetSearchInputgetUpScreenTypeButtongetUpDetailSettingsButtongetUpManagePrefiltersButtongetUpGridSettingsButtongetCubeSortButtongetCubePivotSettingsButtongetCubeChartSettingsButtongetSaveAsCubeViewButtongetDeleteCubeViewButtongetCollapseAllButtongetExpandAllButton
Each method returns a Playwright locator, allowing standard interactions such as:
await event.toolbar3574C040.getSaveButton().click();
await expect(event.toolbar3574C040.getDeleteButton()).toBeVisible();
Form
In model-generated tests, each form is exposed directly on the generated subject (per tab), and can be used either via generated fields (preferred) or via the Form component API for generic interactions.
Thinkwise documents the Form Testwise component and shows usage such as retrieving fields by column id and checking edit mode.
Usage of generated form fields
Generated inputs are available as strongly typed properties on the form:
await <yoursubjecthere>.<yourScreenComponentIdHere>.<yourColumnNamehere>.fill('Your value to fill here');
Example
const event = await page.subject.EventMain;
await event.form2.eventNameField.fill('Test Event');
Usage of Form component methods
The form instance itself is also available from the generated subject:
await <yoursubjecthere>.<yourScreenComponentIdHere>.isInEditMode();
await <yoursubjecthere>.<yourScreenComponentIdHere>.getFieldByColId('col-id__control').fill('Your value to fill here');
Example
const event = await page.subject.EventMain;
await event.form2.isInEditMode();
await event.form2.getFieldByColId('event-name__control').fill('Test Event');
The form instance itself is also available from the generated subject:
Available Methods for the Form component:
- Verify edit mode:
isInEditMode(): Promise<boolean>
const editing = await event.form2.isInEditMode();
// Assert
expect(isInEditMode).toBeTruthy();
- Get field by current value:
getFieldByValue(value: string): Locator
await event.form2.getFieldByValue('Internal').click();
- Get field by column ID:
getFieldByColId(colId: string): Locator
await event.form2.getFieldByColId('event-name__control').fill('Test Event');
- Get lookup button by column ID:
clickInFieldLookup(colId: string): Promise<void>
await event.form2.clickInFieldLookup('category-id__control');
Grid
Access the grid through the generated subject model:
Example
const event = await page.subject.EventMain;
await event.grid1.getCellByExactValue('EVT-2024-Q1-Jan').click();
Hierarchy
Generated grids follow the subject structure:
<subject> → <grid> → <action>
Example
await event.grid1.getRowByIndex(0).click();
Column Cell Collections
In addition to the generic grid methods, model generation can expose typed column cell collections (as Promise<Locator[]>) for direct access to all visible cells in a column.
Example
const nameCells = await event.grid1.eventNameColumnCells;
Available Methods
Navigation and Selection
getColumnHeaderByText(text: string): LocatorgetRowByIndex(rowIndex: number): LocatorgetRowByRowId(rowIdPattern: string): LocatorgetCellByExactValue(value: string): Locatorrows(): LocatorselectRowByRowId(rowIdPattern: string): Promise<void>
Examples:
await event.grid1.getRowByIndex(0).click();
await event.grid1.getCellByExactValue('Some value').click();
await event.grid1.selectRowByRowId('manager');
Filtering
openExcelStyleFilterPopup(columnName: string): Promise<void>filterByColumnValueExcelStyle(columnName: string, valueToSelect: string, deselectAll?: boolean): Promise<void>getRowsByColValue(colIdToMatchOn: string, valueToMatch: string): Promise<Locator[]>
Examples:
await event.grid1.openExcelStyleFilterPopup('Order number');
await event.grid1.filterByColumnValueExcelStyle('Status', 'Active');
const matchingRows = await event.grid1.getRowsByColValue('order_id', '12345');
Extracting Values
getCellValuesByColId(colId: string): Promise<string[]>getColumnHeaderValues(): Promise<string[]>
Examples:
const customerNames = await event.grid1.getCellValuesByColId('customer_name');
const headers = await event.grid1.getColumnHeaderValues();
Validation
verifyGridRecordCount(expectedRecordCount: number): Promise<void>hasNoRowsOverlay(): Promise<boolean>
Examples:
await event.grid1.verifyGridRecordCount(5);
expect(await event.grid1.hasNoRowsOverlay()).toBe(false);
Task bar
In model-generated tests, each Task Bar on a subject is exposed directly on the tab as task1, task2, etc.
To use the Task bar component, access the task bar directly from the generated subject.
Available methods for the Task bar component:
- Returns all task buttons within a task bar:
tasks(): Locator
await expect(event.task1.tasks()).toBeVisible();
- Returns a task button by its ID:
getTaskById(key: string): Locator
const task = event.task1.getTaskById('add-new-event');
await task.click();
- Returns a task button by its visible label:
getTaskByLabel(label: string): Locator
const task = event.task1.getTaskByLabel('Add new event');
await task.click();
- Select a task button by its ID:
clickById(key: string): Promise<void>
const task = event.task1.clickById('add-new-event');
- Select a task button by its visible label:
clickByLabel(label: string): Promise<void>
const task = event.task1.clickByLabel('Add new event');
Task tile
In model-generated tests, each Task Tiles component on a subject is exposed directly on the tab as taskTiles1, taskTiles2, etc. Each property represents one task tile container on the tab.
Available methods for the Task tiles component:
- Returns all task tile buttons :
tasks(): Locator
await expect(event.taskTiles2.tasks()).toBeVisible();
- Returns a task tile by its ID:
getTaskById(key: string): Locator
const task = event.task1.getTaskById('add-new-event');
await task.click();
- Returns a task tile by its visible label:
getTaskByLabel(label: string): Locator
const task = event.task1.getTaskByLabel('Add new event');
await task.click();
- Select a task tile by its ID:
clickById(key: string): Promise<void>
const task = event.task1.clickById('add-new-event');
- Select a task tile by its visible label:
clickByLabel(label: string): Promise<void>
const task = event.task1.clickByLabel('Add new event');
Global Components
There is a list of components that we have made easily accessibile without the need to import or initialize them.
You can use there anywhere where you have a page object, provided the original imported page object that is from Testwise.
The current list of components are:
- export
- listBarMenu
- popUp
- snackBar
- topBar
How to access these component:
await page.components.<chooose component from list>;()
// Example 1:
const pupupHeading = await page.components.popUp.getHeadingText();
// Example 2:
const topBarTitle = await page.components.topBar.getTopBarTitle();
// Example 3:
await page.components.listBarMenu.getMenuGroupByName('My Menu Group').click();
Export
In model-generated tests, the Export component is available via the generated page components collection:
<subject>.page.components.export
The Export dialog must be opened through the toolbar before interacting with the export component.
Depending on the screen width and layout, the Export button may be:
- Visible directly in the toolbar
- Rendered inside the toolbar overflow menu (⋯)
If the button is inside the overflow menu, access it via:
event.toolbar.overflowMenu.getExportButton()
Otherwise, use:
event.toolbar.getExportButton()
Available methods in the Export component:
- Select the export all rows radio button and go to next step:
exportAllRows(): Promise<void>
await event.page.components.export.exportAllRows();
- Select columns to export:
selectColumns(columns: string[]): Promise<void>
const columnsToSelect: string[] = ['FirstColumnName','SecondColumnName','ThirdColumnName'];
await event.page.components.export.selectColumns(columnsToSelect);
- Select the
Nextbutton:nextStep(): Promise<void>
await event.page.components.export.nextStep();
Takes in an array of strings; for every column in the list, a click action is performed on the checkbox matching the column name.
- Select the export format:
selectExportFormat(format: ExportFormat): Promise<void>
import { ExportFormat } from '@thinkwise/testwise';
await exportComponent.selectExportFormat(ExportFormat.CSV)
See Enumerations for more options.
- Complete export:
completeExport(): Promise<Record<string, string[]>>
const exportedData: Record<string, string[]> = await event.page.components.export.completeExport();
// Assert
for (const [index, row] of exportedData.entries()) {
expect(row.Name, `Row ${index} has incorrect value`).toBe('ExpectedValue');
}
Pop-up
To initialize the Pop-up component:
import { PopUpComponent } from '@thinkwise/testwise';
const popup = new PopUpComponent(page);
Available Methods for the Pop-up component:
getHeadingText(): Promise<string | null>getContentText(): Promise<string | null>getContentBody(): LocatorgetActionButtonByText(text: string): Locator
Example
const popup = new PopUpComponent(page);
await popup.getActionButtonByText('Confirm').click();
Custom controls
Initializing these controls provides actions beyond standard Playwright actions.
We have 4 control types currently:
- Lookup dropdown
- Combo Box
- Date Field
- Form Field
The Lookup Dropdown and Combo Box are both variations of a lookups component. They differ somewhat in appearance as well as in behavior under the hood,
but can be clearly identified in Testwise by name
Hierarchy
Generated controls follow the subject structure:
<subject> → <screen component> → <control>
Where:
subjectrepresents the generated subject object, with the screen type attached theretoscreen componentis the screen component identifier. E.g. grid1 or form1controlis the lookup control identifier
Usage
Once you know the control you are targeting you will get both the standard Playwright actions to execute, as well as Testwise specific actions
await <yourSubjectHere>.<yourScreenComponentIdHere>.<yourControlNameHere>.<theActionToPerform>();
Lookup dropdown
In model-generated tests, lookup controls are automatically exposed through the generated subject structure. Each lookup is strongly typed and available directly via the subject’s form hierarchy.
No manual locator definitions or wrapper initialization are required.
Lookup controls are generated with built-in Thinkwise-specific behavior and do not require additional configuration.
Available Method
Over and above the default Playwright actions, you get the following:
lookupSelect(value: string)
Expands the lookup dropdown and then filter-and-selects by the value
await <yourSubjectHere>.<yourScreenComponentIdHere>.<youLookupField>.lookupSelect('<yourOptionHere>');
This method ensures correct interaction with Thinkwise lookup controls and abstracts away underlying UI complexity.
Combo Box
Available Method
Over and above the default Playwright actions, you get the following:
lookupSelect(value: string)
Expands the lookup dropdown and then filter-and-selects by the value
await <yourSubjectHere>.<yourScreenComponentIdHere>.<yourComboBox>.lookupSelect('<yourOptionHere>');
getValidationMessage()
Retrieves the current validation message that has fired
const validationMessage = await <yourSubjectHere>.<yourScreenComponentIdHere>.<youComboBox>.getValidationMessage();
clearByClick()
Clears the ComboBox selection by clicking the x button right of the currently selected option
await <yourSubjectHere>.<yourScreenComponentIdHere>.<youComboBox>.clearByClick();
Form Field
The form field gives you access to helpful methods that you do not need to build out separately such as getting the label text and retrieving validation messages
Available Method
InputElement()
Allows you to perform actions directly to the input nested within the field.
Performing actions like fill, clear, or inputValue can be done on the form field directly, and will propagate down into the input element.
But as seen in the example below, if you want to assess a particular attribute on specifically the input and not the parent field container,
then you need to access the Input via the InputElement() method
await expect(<yourSubjectHere>.<yourScreenComponentIdHere>.<yourFormFieldName>.InputElement()).toHaveAttribute('type', 'text');
getLabelText()
Retrieves the text value of the label
const labelText = await <yourSubjectHere>.<yourScreenComponentIdHere>.<yourFormFieldName>.getLabelText();
getValidationMessage()
Retrieves the current validation message that has fired
const validationMessage = await <yourSubjectHere>.<yourScreenComponentIdHere>.<yourFormFieldName>.getValidationMessage();
Date Field
The Date Field currently works the same as any other Locator object,
with the fill Action having special functionality built in, to make it compatible with filling dates into the date field.
Enumerations
Enumerations are used to define a set of named constants. You can use the enumerations below in your test scripts.
Export formats:
export enum ExportFormat {
CSV = 'csv',
XLSX = 'xlsx',
XLS = 'xls'
}
Types
Universal login options:
export type UniversalLoginOptions = {
username?: string;
password?: string;
serviceUrl?: string;
metaEndpoint?: string;
config?: UniversalConfigOptions;
};
Universal config options:
export type UniversalConfigOptions = {
barcodeScannerSymbologies?: string;
cortexEnabledSymbologies?: string[];
cortexLicense?: string;
debugMode?: boolean;
devDisableProcessFlows?: boolean;
useFormFieldBackgroundColor?: boolean;
defaultApplication?: string;
defaultPlatform?: number;
loginOptionsDisabled?: boolean;
loginOptionsHidden?: boolean;
installNotificationDisabled?: boolean;
enableDragDrop?: boolean;
installNotificationExpirationInDays?: number;
spacingMode?: string;
serviceUrl?: string;
useServiceWorker?: boolean;
};
Did not find what you were looking for?
For anything else you need to do which we do not cater for specifically, you can review the Playwright documentation for available functionality.