_serenity-js
TL;DR - Show Me The Code
import { describe, it } from '@serenity-js/playwright-test';
import { Ensure, equals, not } from '@serenity-js/assertions';
import { notes } from '@serenity-js/core';
import { By, Clear, Enter, Navigate, PageElement, Switch, Text } from '@serenity-js/web';
describe('Frames', () => {
describe('Example 1', () => {
const frames = {
top: PageElement.located(By.css('[name="frame-top"]')),
middle: PageElement.located(By.css('[name="frame-middle"]')),
content: PageElement.located(By.id('content')),
};
it('works with nested frames', async ({ actor }) => {
await actor.attemptsTo(
Navigate.to('https://the-internet.herokuapp.com/nested_frames'),
Switch.to(frames.top).and(
Switch.to(frames.middle).and(
Ensure.that(Text.of(frames.content), equals('MIDDLE')),
),
),
);
});
});
describe('Example 2', () => {
interface EditorText {
before: string;
after: string;
}
const tinyMceComponent = {
heading: PageElement.located(By.css('h3')),
frame: PageElement.located(By.id('mce_0_ifr')),
editor: PageElement.located(By.id('tinymce')),
}
it('works with a TinyMCE WYSIWYG Editor', async ({ actor }) => {
await actor.attemptsTo(
Navigate.to('https://the-internet.herokuapp.com/tinymce'),
Switch.to(tinyMceComponent.frame).and(
notes<EditorText>().set('before', Text.of(tinyMceComponent.editor)),
Clear.theValueOf(tinyMceComponent.editor),
Enter.theValue('Hello World!').into(tinyMceComponent.editor),
notes<EditorText>().set('after', Text.of(tinyMceComponent.editor)),
Ensure.that(notes<EditorText>().get('before'), not(equals(notes<EditorText>().get('after')))),
Ensure.that(notes<EditorText>().get('before'), equals('Your content goes here.')),
Ensure.that(notes<EditorText>().get('after'), equals('Hello World!')),
),
Ensure.that(Text.of(tinyMceComponent.heading), equals('An iFrame containing the TinyMCE WYSIWYG Editor')),
);
});
});
});
Code Walkthrough
This example uses Serenity/JS and Playwright Test to demonstrate how to work with frames in a web application.
To learn more about Serenity/JS, check out:
- Web testing with Serenity/JS
- Serenity/JS Screenplay Pattern
- Serenity/JS Assertions
- Serenity/JS Playwright Test integration
Importing Libraries, Setup and Teardown
First, we import the describe
and it
functions from
@serenity-js/playwright-test
- the Serenity/JS adapter for Playwright Test.
We'll use these functions to group and define our test cases, respectively,
as they offer integration with Serenity/JS reporters
and test fixtures that allow us to use Serenity/JS Screenplay Pattern
libraries in our test scenarios.
Relying on Serenity/JS fixtures enables us
to avoid defining the setup and teardown logic for our tests, as it's all done automatically by the test runner.
Later, we import Serenity/JS Screenplay Pattern libraries that we'll use to interact with the web app
and perform assertions.
Note that the @serenity-js/web
module offers an abstraction layer over web integration
tools like Selenium, Playwright, or WebdriverIO, allowing us to write our tests in a driver-agnostic way and swap out
the driver without having to modify the tests.
Furthermore, the @serenity-js/assertions
module provides an universal
assertion library that enables us to write our assertions using the same syntax regardless of the interface
our tests interact with - be it web UI, REST API, or anything else.
Example 1
Our first test scenario is interacting with the nested frames example from the-internet.
While Selenium, WebdriverIO, and Playwright all approach frame switching differently and have their own methods for it, Serenity/JS provides a single, consistent API for working with frames, regardless of the underlying driver.
Afterwards, to perform activities in the context of a frame, we use the interaction to Switch
from the
@serenity-js/web
module, and provide a PageElement
that represents the frame we want to work with.
When our actor
finishes their last activity in the frame,
they'll automatically switch their focus back to the parent frame or the top-level page, depending on the context.
This way, we can avoid the need to keep track of frames and manually switch back after we're done with the nested frame.
If we need to work with frames nested in other frames, like the middle frame in this example, we repeat the same pattern
to get to the desired frame, then retrieve the text of the desired element, to then finally perform the assertion.
When the actor
is done, they'll automatically switch their focus back to the top-level frame, and then the top-level page.
Example 2
A common scenario where you'll likely run into frames involves working with WYSIWYG editors, such as TinyMCE.
Our second test scenario is interacting with the TinyMCE editor embedded in a nested frame.
You'll notice that apart from interacting with the frame, this example also demonstrates how to make you actor
use their notes()
to store and retrieve information about the state of the application under test.
Once the page loads:
- We switch into the frame that contains TinyMCE
- Then, store the original text displayed in the editor and store it in the actor's notes
- Next, use the interaction to
Clear
to remove the contents of the editor - Afterward, use the interaction to
Enter
to enter the new value - Finally, use the interaction to
Ensure
to assert that the new value is different than what the actor saw originally
When the activity sequence from is complete, the actor automatically switches their focus back to the top-level page. This mechanism allows us to go straight to the next step of the scenario without having to worry about the frames, and so it ensures that the content of the top-level page heading is as expected.
Executing the Test
Before executing the test, we need to make sure the required dependencies are declared on the package.json
file.
Toggle to see the package.json
file.
{
"name": "serenity-js-elemental-automation-work-with-frames",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "playwright test",
"postinstall": "playwright install --with-deps"
},
"license": "MIT",
"homepage": "https://serenity-js.org",
"dependencies": {
"@playwright/test": "1.48.2",
"@serenity-js/assertions": "3.29.5",
"@serenity-js/core": "3.29.5",
"@serenity-js/console-reporter": "3.29.5",
"@serenity-js/playwright-test": "3.29.5",
"@serenity-js/web": "3.29.5"
},
"devDependencies": {
"@types/node": "22.9.0",
"typescript": "5.6.3"
}
}
Finally, we can run the test by executing npm test
from the command-line.