{"id":15233847,"url":"https://github.com/hamedstack/hamedstack.playwright.screenplay","last_synced_at":"2025-04-10T06:11:38.378Z","repository":{"id":197845151,"uuid":"694760794","full_name":"HamedStack/HamedStack.Playwright.Screenplay","owner":"HamedStack","description":"A screenplay pattern designed for Playwright, equipped with complete TypeScript support.","archived":false,"fork":false,"pushed_at":"2023-10-02T22:38:48.000Z","size":115,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-01T11:44:12.317Z","etag":null,"topics":["javascript","node","node-js","nodejs","playwright","playwright-javascript","playwright-tests","playwright-typescript","screenplay","screenplay-pattern","typescript","utilities","utility","utility-library"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HamedStack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-09-21T16:29:27.000Z","updated_at":"2025-03-10T07:59:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"eb33ab0b-70ae-44d3-aa24-b09a9ce1f4f5","html_url":"https://github.com/HamedStack/HamedStack.Playwright.Screenplay","commit_stats":null,"previous_names":["hamedstack/hamedstack.playwright.screenplay"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HamedStack%2FHamedStack.Playwright.Screenplay","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HamedStack%2FHamedStack.Playwright.Screenplay/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HamedStack%2FHamedStack.Playwright.Screenplay/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HamedStack%2FHamedStack.Playwright.Screenplay/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HamedStack","download_url":"https://codeload.github.com/HamedStack/HamedStack.Playwright.Screenplay/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248166925,"owners_count":21058481,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","node","node-js","nodejs","playwright","playwright-javascript","playwright-tests","playwright-typescript","screenplay","screenplay-pattern","typescript","utilities","utility","utility-library"],"created_at":"2024-09-29T06:08:25.807Z","updated_at":"2025-04-10T06:11:38.355Z","avatar_url":"https://github.com/HamedStack.png","language":"TypeScript","readme":"![playwright](https://user-images.githubusercontent.com/8418700/220898294-ec067d4d-c65c-43ab-96d7-8fdf52d0a6a7.png)\n\n## What is a Screenplay Pattern?\n\nThe Screenplay Pattern is a user-centric approach to writing workflow-level automated acceptance tests. This helps automation testers to write test cases in terms of Business language.\n\n## The Design\n\n![the-screenplay-pattern](https://user-images.githubusercontent.com/8418700/221844273-163cbfa5-f964-413b-9055-08771e248200.png)\n\nThe Screenplay Pattern can be summarized in one line: \n\n```\nActors use Abilities to perform Interactions.\n```\n\n* Actors initiate Interactions.\n* Abilities enable Actors to initiate Interactions.\n* Interactions are procedures that exercise the behaviors under test.\n    * Tasks execute procedures on the features under test.\n    * Questions return state about the features under test.\n    * Interactions may use Locators, Requests, and other Models.\n\n\n**Actors:** Actors are the main entities in the Screenplay Pattern. They represent the different types of users who\ninteract with the system. Each actor is responsible for performing specific tasks and interacting with the\nsystem in a specific way. For example, an actor could represent a regular user, an administrator, or a\ncustomer support representative.\n\n**Interactions:** Interactions are the actions that an actor performs on the system. They represent the different\nways that an actor can interact with the system, such as clicking a button, entering data into a form, or\nnavigating to a specific page.\n\n**Questions:** Questions are used to retrieve information from the system. They represent the different types of\ninformation that an actor might need to retrieve during the course of their interactions with the system. For\nexample, a question could be used to retrieve the text of an error message or the value of a specific field in a\nform.\n\n**Tasks:** Tasks are the main building blocks of the Screenplay Pattern. They represent the different activities that\nan actor performs in order to achieve a specific goal. A task can consist of one or more interactions and\nquestions, and can be used to model complex workflows and user journeys.\n\n**Abilities:** Abilities represent the different capabilities that an actor has. They include things like the ability to\ninteract with the system using a specific user interface, the ability to read and write data to a database, and\nthe ability to send and receive messages over a network.\n\n| Concepts       | Differences       |\n|----------------|----------------|\n| Tasks vs. Interactions | The primary difference between tasks and interactions is their granularity. Tasks represent high-level actions that an actor performs, while interactions represent the individual steps or actions that make up a task. For example, a task might be \"Login to the system\", while the interactions that make up that task might include entering a username, entering a password, and clicking the \"Login\" button. |\n| Abilities vs. Interactions | While both abilities and interactions represent actions that an actor can perform, they serve different purposes. Abilities represent the skills or capabilities that an actor possesses, while interactions represent the specific actions that an actor performs in order to complete a task. Abilities are typically called inside interactions, as they represent a specific skill or capability that an actor possesses. Interactions are actions that the actor performs using their abilities. |\n| Tasks vs. Questions | Tasks and questions are closely related, but they serve different purposes. Tasks represent the actions that an actor performs, while questions represent the verifications that the actor performs after completing those actions. In other words, tasks are about doing, while questions are about verifying. |\n| Actors vs. Abilites | Actors and abilities are also closely related, but they serve different purposes. Actors represent the users or personas who interact with the system being tested, while abilities represent the skills or capabilities that those actors possess. In other words, actors are the \"who\" of the system being tested, while abilities are the \"what\". |\n\n## The Principles\n\nThe Screenplay Pattern adheres to **SOLID** design principles:\n\n| SOLID Principle                 | Explanation    |\n|---------------------------------|----------------|\n| Single-Responsibility Principle | Actors, Abilities, and Interactions are treated as separate concerns. |\n| Open-Closed Principle           | Each new Interaction must be a new class, rather than a modification of an existing class. |\n| Liskov Substitution Principle   | Actors can call all Abilities and Interactions the same way. |\n| Interface Segregation Principle | Actors, Abilities, and Interactions each have distinct, separate interfaces. |\n| Dependency Inversion Principle  | Interactions use Abilities via dependency injection from the Actor. |\n\n\n## Example\n\nHere's an example scenario for adding a new item to a todo list:\n\n![image](https://user-images.githubusercontent.com/8418700/221852171-59d0f5f3-6d9e-4af4-86d1-017a17de69cf.png)\n\n* A user navigates to the to-do list page. (An interaction like VisitPage)\n* A user sees the \"Add Item\" button (+) and clicks it. (An interaction like ClickOnAddButton)\n* A user enters the title (My todo) of the new item into the input field and presses enter from the keyboard. (An interaction like AddTodoItem)\n* A user sees the last item on the to-do list which is the newly added one. (A question like GetLastTodoItem)\n\n```typescript\n// Ability\n// Abilities to work with Playwright: UsePlaywrightPage, UsePlaywrightBrowser, or UsePlaywrightBrowserContext.\n// Defined inside library but you can define what you want.\n\n/*\n// For example:\nexport class UseSqlDatabase extends Ability\u003cDbConnection\u003e {\n    constructor(private connectionString: string) {\n        super();\n    }\n    can(): DbConnection {\n        return new SqlDatabase(connectionString);\n    }\n}\n*/\n\n// Interactions\nexport class VisitPage extends Interaction {\n  async attemptAs(actor: Actor): Promise\u003cvoid\u003e {\n    let page = await actor.useAbility(UsePlaywrightPage); // Use an abilitiy to interact with what you want.\n    await page.goto(\"http://...\");\n  }\n}\nexport class ClickOnAddButton extends Interaction {\n  async attemptAs(actor: Actor): Promise\u003cvoid\u003e {\n    let page = await actor.useAbility(UsePlaywrightPage);\n    await page.locator(\"i.fa-plus\").click();\n  }\n}\n\nexport class AddTodoItem extends Interaction {\n  async attemptAs(actor: Actor) {\n    let page = await actor.useAbility(UsePlaywrightPage);\n    await page.getByTestId('value').type('My first todo');\n    await page.keyboard.press('Enter');\n  }\n}\n\n// Task\n// Executing all interactions in order. (less control)\nexport class AddTodoTask extends Task {\n  constructor() {\n    super([new VisitPage(), new ClickOnAddButton(), new AddTodoItem()]);\n  }\n  public async performAs(actor: Actor): Promise\u003cvoid\u003e {\n    await this.attemptInteractionsAs(actor);\n  }\n}\n\n// Executing all interactions based on QA/Developer idea. (more control)\nexport class AddTodo extends Task {\n  constructor() {\n    super([new VisitPage(), new ClickOnAddButton(), new AddTodoItem()]);\n  }    \n  public async performAs(actor: Actor): Promise\u003cvoid\u003e {\n    await this.attemptInteractionAs(actor, VisitPage); // You can get a return value and assert on it if you want.\n    await this.attemptInteractionAs(actor, ClickOnAddButton);\n    await this.attemptInteractionAs(actor, AddTodoItem);\n  }\n}\n\n// Question\nexport class GetLastTodoItem extends Question {\n  // Always returns a value to write assertions based on it.\n  async askAs(actor: Actor): Promise\u003cstring\u003e {\n    let page = await actor.useAbility(UsePlaywrightPage);\n    return await page.getByTestId('todo').last().innerText();\n  }\n}\n\n// Test\ntest('add a new item to todo list', async ({ page }) =\u003e {\n  const pw = new UsePlaywrightPage(page);\n  // const sqlDb = new UseSqlDatabase(\"...\");\n\n  // Pass abilities to the ctor\n  let user = new Actor([pw /*, sqlDb*/]); // Our user\n\n  await user.performs(new AddTodoTask()); // Executes a task, an interaction, an interactions, or a tasks.\n  // await user.performs(new AddTodo());\n\n  // Question \u0026 Assertion separately.\n  let theAnswer = await user.asksAbout(new GetLastTodoItem()) as string; // What is the value of last todo item? (system state)\n  expect(theAnswer).toBe(\"My first todo\"); // Making sure about the answer/state.\n  \n  // Question \u0026 Assertion together.\n  await user.asserts(new GetLastTodoItem(), (answer: string) =\u003e {\n    expect(answer).toBe(\"My first todo\");\n  });\n});\n```\n\n## References\n\n1. https://serenity-js.org/handbook/design/screenplay-pattern.html\n2. https://q2ebanking.github.io/boa-constrictor/getting-started/screenplay/\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhamedstack%2Fhamedstack.playwright.screenplay","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhamedstack%2Fhamedstack.playwright.screenplay","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhamedstack%2Fhamedstack.playwright.screenplay/lists"}