Playwright MCP

AI-Powered Playwright MCP in Action: Page Objects, Shadow DOM, and Smart Refactoring

In a previous blog post, I highlighted Playwright MCP. If you haven’t read it yet, I encourage you to take a look. In the article, I demonstrated how to integrate Playwright MCP with our IDE and create a simple test using it. Alongside practical examples, I mentioned potential pitfalls and limitations of this approach.

Today, I’ll share additional ways to use Playwright MCP in daily work.

Getting started with Playwright MCP

As I mentioned in the previous article, this is currently the most widely used MCP implementation for Playwright. As you can see, there are plenty of useful resources, such as YouTube videos, articles on LinkedIn, and blog posts demonstrating how to use the library.

Other Playwright MCP Examples

Generating Page Objects?

One of the most interesting uses of Playwright MCP is the automatic generation of page objects. We need only provide a prompt for a specific page, based on the HTML DOM, and the tool can generate a reasonably accurate page object.

Obviously, this comes with potential pitfalls – for instance, the object may become overly complex with lots of unnecessary methods. Despite this, in many cases, Playwright MCP can significantly speed up our work on a test automation framework.

For this demo, I’m going to use one of my sample pages –  a simple login form created for workshop purposes.

https://jolly-praline-36a7fb.netlify.app/

The login form is straightforward, consisting of two text fields and a login button.

Using Cursor and Playwright, I demonstrated how to generate the page object

I previously covered the topic of generating page objects with LLMs in one of my previous posts (link), but in this case, I want to highlight the potential that Playwright MCP offers.

Even if Playwright MCP won’t ultimately be used to generate all automated tests, this functionality can still be very useful—especially at the early stages of building a test automation framework.

For now, I use Playwright MCP as an experimental tool, and I encourage you to stay sharp and keep developing your skills — but it may well become popular in the future.

How does Playwright MCP work with Shadow DOM?

Shadow DOM is often a challenge for UI automated tests; however, we now have solutions for dealing with it. Playwright handles Shadow DOM without any issues, but is it the same for Playwright MCP? Let’s find out.

Playwright MCP Shadow DOM

On the https://books-pwakit.appspot.com/explore page, there is a form that uses this UI control. In Cursor, I enter a prompt with the information that I’m looking for the book “Electricity and Magnetism”. Afterwards, in the same prompt, I add the next actions: clicking the “Preview” button, then verifying whether the “buy this book” button is available.

My prompt:

You are a test automation engineer using Playwright and the MCP (Model Context Protocol) approach.
Your task is to automate the following flow on the website https://books-pwakit.appspot.com/explore:
1. Locate the input field to search for books. This is a custom web component.
2. Enter the book title: `Electricity and Magnetism`.
3. Wait for the search results to load.
4. Find the correct result (the book titled “Electricity and Magnetism”) and click the “Preview” button associated with it.
5. Wait for the preview to be visible.
6. Verify that the button labeled “Buy this book” is visible in the preview section.
Use Playwright with TypeScript and follow the MCP structure (Page Object Model with clear locators and actions). Implement proper waiting logic, not fixed delays.
generate:
– A Page Object for the ExploreBooksPage with methods for search, preview, and checking the buy button.
– A test that uses this Page Object to perform the described flow.

Playwright MCP Window Context
Playwright MCP Ideas of improvements

The code generated by LLM contains elements that don’t meet my expectations. In the prompt, the model created an individual method for each action, instead of treating them as business operations. For example, methods like clickPreviewButton or verifyBuyButton, which are added to the page object alongside other interaction-specific methods. This style is something we often encounter in daily work.

Other ideas?

Nevertheless, from my point of view, a page object should mainly contain locators and methods for business actions. Creating methods like clickPreviewButton is often style over substance. An even bigger problem is moving the assertion layer into the page object. In the case of the verifyBuyButton method, the name doesn’t clearly indicate what it checks—whether the element is visible or enabled on the page. From an architectural point of view, the better solution is to move assertions into the test code. Assertions should be explicit in test code and not hidden inside page object methods. If we encounter more complex situations that require a custom assertion method, we can create another class for these methods, but the call should remain in the test.

On the positive side, the generated code uses test.step, which helps us understand the next parts of a test scenario, and simplifies analyzing test execution in the Trace Viewer. It’s a good practice because it lets us see, step by step, which operations are performed. Certainly, it’s worth considering the frequency of using test.step so as not to clutter the code.

The whole situation shows how important it is to constantly develop our technical skills. A lack of appropriate knowledge can lead the LLM to propose suboptimal—or even incorrect—solutions. Even though we may have experience and can phrase our prompt precisely, the generated code could still be much more readable and easier to maintain.

 The generated code:

Playwright MCP Sample Code

Interestingly, I set a small trap in the task for LLM. I knew that the “Buy this book” button is displayed in different languages depending on the browser’s language settings. Since the default language on my computer is Polish, the model took this context into account when generating the code.

In this case, a better solution would be to use a stable selector that is not based on the text.

Playwright MCP Locators

Interestingly, the tool eventually provides a working example.

Refactoring of the code & prompt– What can we improve?

Consider how we should define a follow-up prompt to avoid these issues.

Let’s think about how to define this prompt so that these errors and inconsistencies are resolved by the LLM. I’ve prepared a prompt for that:

Refactor the following code according to these rules:

  • Keep selectors as string values outside the constructor, and do not initialize them in the constructor.
  • Avoid over-abstraction (e.g., no methods like clickPreviewButton or verifyBuyButtonIsVisible).
  • Remove all comments.
  • Do not use locators with or conditions.
  • Do not use waitForLoadState or static waits (e.g., waitForTimeout).
  • Use MCP to improve selectors.
  • Base selectors on stable attributes, not text.
  • Link to the target app: https://books-pwakit.appspot.com.
  • Keep verification logic in the test, not in the page object.

After performing all actions, we see what LLM has done:

Selectors Beyond The Constructor – Why?

  • Growing Constructor — if we keep a selector in the constructor for each element, the code can quickly become bloated. The class becomes less readable and unnecessarily large.
  • Lazy Element Lookup  — In getters or string fields, the selector is initialized only when the element is actually used. This way, we can be sure the element is accessed only when needed, not when the object is created. This reduces the risk of potential issues and makes tests more stable.

Avoid an Additional Level of Abstraction

  • It’s not worth creating methods for everything — clickPreviewButton() may sound fine, but in reality, it does nothing more than locator.click().
  • Additional methods only make sense when they add real value — for example, clickWithWait() — where the extra logic provides justification.
  • Simple actions (clicking, filling text) — are best used directly on the Locator object. The code becomes shorter, clearer, and free of redundant layers of abstraction.

Comments

  • The exception, not the standard — code comments should be used only when the logic is tricky to understand.
  • Self-explanatory code > comments — Clear method and variable names, along with a simple structure, are usually enough.
  • When is it worth it? — During the learning process, when performing experiments, or in exceptionally complex business logic.

Avoid Using “or” in Selectors

  • Multilingual scenarios — If our page supports multiple languages, adding conditions like locator.getByText("Buy this book").or("Kup tę książkę") quickly becomes hard to read.
  • Better solutions:
    • Clear CSS or XPath selectors – I want to emphasize the word “clear”, because these selectors can easily become too complex. However, when kept simple, they can be very handy.
    • Data-testid — A useful approach if the team agrees to add these attributes consistently to all elements.Roles (getByRole) – in accordance with ARIA and accessibility standards.
    • Structured HTML — based on stable and unique HTML elements
  • Using text selectors can make sense when we want to test text content.
  • Visual Regression or accessibility snapshots are better ways to verify whether a particular text is visible on the page.

No static waits

  • Playwright has a built-in autowait mechanism that handles most conditions. However, there are cases where additional wait methods may be required for specific resources.  
  • What to avoid:
    • waitForTimeout — slows down operations and doesn’t resolve the real problem.
    • waitForLoadState("networkidle") — often doesn’t reflect what page readiness really means.
  • Better options:
    • Expect(locator).toBeVisible() — combines retry and auto-wait in a single method.Locator.waitFor() – when we need to wait for a specific element.
    • toPass() — useful when we need a more complex waiting mechanism.

Use Playwright MCP

  • During the test creation process, Playwright MCP can analyze the page structure and suggest stable selectors.
  • Sometimes our experience suggests a better solution, but Playwright MCP can still speed up our work and provide fresh ideas.
  • It’s worth using Playwright MCP as a helper – it may not always provide the best solution, but in many cases it’s very useful.

Stable Selector

  • Avoid indexes (first(), last()) — Today we may have one product, tomorrow ten, and our tests will fail.
  • Think in terms of the business domain — a unique method name is more stable than “ClickFirstButtonPlus”
  • Create generic selectors independent of the number of elements, they should work in all conditions.

Assertions in the test code

  • Single Responsibility Principle:
    • The Page Object should contain locators and business actions.
  • Don’t mix responsibilities — Page Objects shouldn’t contain assertions like verifyBuyButton(). It’s often unclear what such methods verify.
  • If we need to use more complex assertions — for example, comparing multiple fields — it’s a good idea to create a separate class with these methods, but the method calls should still be made in the test.

Dynamic waits for resources. Playwright doesn’t have a “magic” waitUntilEverythingIsReady().

  • The best approach:
    • Wait for specific elements — (expect(locator)...),
    • Wait for API responses (waitForResponse()),
  • It’s better to define what page readiness means for a given page instead of relying on generic methods.

Summary – useful practices in Playwright

  1. Keep selectors beyond the constructor  — clearer code and better initialization
  2. Avoid methods like clickXxx() — only use them when they add real value
  3. Use comments only when necessary —Think twice before adding one: if our code is clear, does it really need a comment?
  4. Don’t rely on text or “OR” methods — stable selectors are better than text-based ones. Text can be used in assertions to verify expected content or if the page supports only one language version. However, in most cases, it’s better to rely on stable, not text-based locators.
  5. Zero Use of Static Waits — rely on auto-waits and retryable assertions.
  6. Use assertions in the test code — page object has locators and actions. The test contains assertions.

After refactoring, the code looks better. However, I stumbled upon a problem: the test doesn’t include dynamic waits for resources that should be visible.

I added additional prompts to properly wait for elements and avoid relying on text like “buy this book” or in Polish “kup tę książkę”.

How does the code look now?

Playwright MCP one of these tests
Playwright MCP Refactored test

Generated page object – ExploreBooksPage class

Playwright MCP generated page

The code looks cleaner after refactoring. When working with Playwright MCP, you need to be mindful of potential pitfalls and learn how to avoid them. It’s worth remembering that we can almost always improve something—but in some cases, it’s better to accept the current quality as “good enough” for the context. That said, I still see areas for improvement. For example, the getFirstBookTitle method contains an assertion using expect.toHaveText(). In this case, it behaves more like an auto-wait, since toHaveText() it is an auto-retrying assertion that stabilizes the test.

Execution of these tests

After applying all improvements, the tests pass successfully.

Playwright MCP passed tests

Summary

In today’s post, I shared a few ideas for using Playwright MCP. These included working with Shadow DOM and generating Page Objects for our pages. I also highlighted how important solid technical knowledge is, as it allows you to create tests that are stable and easy to maintain.

Lack of this knowledge can lead to tests not working at all, or even if they do, being very susceptible to the slightest changes in the site structure.

The ideas for using Playwright MCP and LLM are interesting, but it’s worth remembering:

  • Verifying the effects of our experiments – not everything that is generated by LLM will be optimal or worth using.
  • The quality of prompts is crucial – a well-prepared prompt is the first step to better and more maintainable code.

See What’s On: Workshops & Consultations

Level up your skills — see current workshops!

Newsletter

Author

Michał Ślęzak

Michał Ślęzak (Michal Slezak) brings over 10 years of expertise in test automation, having led projects for prominent banks and contributed to aviation and other major industry initiatives. His experience spans from large enterprises to innovative startups, including ArtistGrowth and WhatClinic.com. Currently, as a Test Architect at Sii, Michal plays a key role in diverse projects and initiatives. He also shares his knowledge through his blog, testingplus.me, focused on test automation in Polish, and actively engaged in the testing community by delivering training sessions and speaking at renowned conferences like QA Global Summit, 4Developers, TestWarez, TestFest, ConSelenium, and TestCamp.

Related posts:

Playwright in Practice: Writing Better Tests for Beginners with Page Object Pattern, Fixtures (TS)

Recently, I’ve been writing about using AI tools with Playwright — for example, how to use Cursor with Playwright (link), Playwright MCP (link), and how AI can help generate simple page objects. In today’s post, I’ll walk you through a complete refactoring: taking tests that don’t use the Page Object pattern or other object-oriented principles ...

Read more

Smarter Slides with AI Presentation Tools: My Experiments with 4 Useful Tools

As you’ve probably noticed, AI hype is everywhere right now, and almost every tool comes with a bunch of AI features – or at least it’s written on the landing page. Companies keep emphasizing that they use AI, LLMs, or at least machine learning. In this article, I’ll review four AI presentation tools I personally ...

Read more

Playwright MCP

AI-Powered Playwright MCP in Action: Page Objects, Shadow DOM, and Smart Refactoring

In a previous blog post, I highlighted Playwright MCP. If you haven’t read it yet, I encourage you to take a look. In the article, I demonstrated how to integrate Playwright MCP with our IDE and create a simple test using it. Alongside practical examples, I mentioned potential pitfalls and limitations of this approach.

Today, I’ll share additional ways to use Playwright MCP in daily work.

Getting started with Playwright MCP

As I mentioned in the previous article, this is currently the most widely used MCP implementation for Playwright. As you can see, there are plenty of useful resources, such as YouTube videos, articles on LinkedIn, and blog posts demonstrating how to use the library.

Other Playwright MCP Examples

Generating Page Objects?

One of the most interesting uses of Playwright MCP is the automatic generation of page objects. We need only provide a prompt for a specific page, based on the HTML DOM, and the tool can generate a reasonably accurate page object.

Obviously, this comes with potential pitfalls – for instance, the object may become overly complex with lots of unnecessary methods. Despite this, in many cases, Playwright MCP can significantly speed up our work on a test automation framework.

For this demo, I’m going to use one of my sample pages –  a simple login form created for workshop purposes.

https://jolly-praline-36a7fb.netlify.app/

The login form is straightforward, consisting of two text fields and a login button.

Using Cursor and Playwright, I demonstrated how to generate the page object

I previously covered the topic of generating page objects with LLMs in one of my previous posts (link), but in this case, I want to highlight the potential that Playwright MCP offers.

Even if Playwright MCP won’t ultimately be used to generate all automated tests, this functionality can still be very useful—especially at the early stages of building a test automation framework.

For now, I use Playwright MCP as an experimental tool, and I encourage you to stay sharp and keep developing your skills — but it may well become popular in the future.

How does Playwright MCP work with Shadow DOM?

Shadow DOM is often a challenge for UI automated tests; however, we now have solutions for dealing with it. Playwright handles Shadow DOM without any issues, but is it the same for Playwright MCP? Let’s find out.

Playwright MCP Shadow DOM

On the https://books-pwakit.appspot.com/explore page, there is a form that uses this UI control. In Cursor, I enter a prompt with the information that I’m looking for the book “Electricity and Magnetism”. Afterwards, in the same prompt, I add the next actions: clicking the “Preview” button, then verifying whether the “buy this book” button is available.

My prompt:

You are a test automation engineer using Playwright and the MCP (Model Context Protocol) approach.
Your task is to automate the following flow on the website https://books-pwakit.appspot.com/explore:
1. Locate the input field to search for books. This is a custom web component.
2. Enter the book title: `Electricity and Magnetism`.
3. Wait for the search results to load.
4. Find the correct result (the book titled “Electricity and Magnetism”) and click the “Preview” button associated with it.
5. Wait for the preview to be visible.
6. Verify that the button labeled “Buy this book” is visible in the preview section.
Use Playwright with TypeScript and follow the MCP structure (Page Object Model with clear locators and actions). Implement proper waiting logic, not fixed delays.
generate:
– A Page Object for the ExploreBooksPage with methods for search, preview, and checking the buy button.
– A test that uses this Page Object to perform the described flow.

Playwright MCP Window Context
Playwright MCP Ideas of improvements

The code generated by LLM contains elements that don’t meet my expectations. In the prompt, the model created an individual method for each action, instead of treating them as business operations. For example, methods like clickPreviewButton or verifyBuyButton, which are added to the page object alongside other interaction-specific methods. This style is something we often encounter in daily work.

Other ideas?

Nevertheless, from my point of view, a page object should mainly contain locators and methods for business actions. Creating methods like clickPreviewButton is often style over substance. An even bigger problem is moving the assertion layer into the page object. In the case of the verifyBuyButton method, the name doesn’t clearly indicate what it checks—whether the element is visible or enabled on the page. From an architectural point of view, the better solution is to move assertions into the test code. Assertions should be explicit in test code and not hidden inside page object methods. If we encounter more complex situations that require a custom assertion method, we can create another class for these methods, but the call should remain in the test.

On the positive side, the generated code uses test.step, which helps us understand the next parts of a test scenario, and simplifies analyzing test execution in the Trace Viewer. It’s a good practice because it lets us see, step by step, which operations are performed. Certainly, it’s worth considering the frequency of using test.step so as not to clutter the code.

The whole situation shows how important it is to constantly develop our technical skills. A lack of appropriate knowledge can lead the LLM to propose suboptimal—or even incorrect—solutions. Even though we may have experience and can phrase our prompt precisely, the generated code could still be much more readable and easier to maintain.

 The generated code:

Playwright MCP Sample Code

Interestingly, I set a small trap in the task for LLM. I knew that the “Buy this book” button is displayed in different languages depending on the browser’s language settings. Since the default language on my computer is Polish, the model took this context into account when generating the code.

In this case, a better solution would be to use a stable selector that is not based on the text.

Playwright MCP Locators

Interestingly, the tool eventually provides a working example.

Refactoring of the code & prompt– What can we improve?

Consider how we should define a follow-up prompt to avoid these issues.

Let’s think about how to define this prompt so that these errors and inconsistencies are resolved by the LLM. I’ve prepared a prompt for that:

Refactor the following code according to these rules:

  • Keep selectors as string values outside the constructor, and do not initialize them in the constructor.
  • Avoid over-abstraction (e.g., no methods like clickPreviewButton or verifyBuyButtonIsVisible).
  • Remove all comments.
  • Do not use locators with or conditions.
  • Do not use waitForLoadState or static waits (e.g., waitForTimeout).
  • Use MCP to improve selectors.
  • Base selectors on stable attributes, not text.
  • Link to the target app: https://books-pwakit.appspot.com.
  • Keep verification logic in the test, not in the page object.

After performing all actions, we see what LLM has done:

Selectors Beyond The Constructor – Why?

  • Growing Constructor — if we keep a selector in the constructor for each element, the code can quickly become bloated. The class becomes less readable and unnecessarily large.
  • Lazy Element Lookup  — In getters or string fields, the selector is initialized only when the element is actually used. This way, we can be sure the element is accessed only when needed, not when the object is created. This reduces the risk of potential issues and makes tests more stable.

Avoid an Additional Level of Abstraction

  • It’s not worth creating methods for everything — clickPreviewButton() may sound fine, but in reality, it does nothing more than locator.click().
  • Additional methods only make sense when they add real value — for example, clickWithWait() — where the extra logic provides justification.
  • Simple actions (clicking, filling text) — are best used directly on the Locator object. The code becomes shorter, clearer, and free of redundant layers of abstraction.

Comments

  • The exception, not the standard — code comments should be used only when the logic is tricky to understand.
  • Self-explanatory code > comments — Clear method and variable names, along with a simple structure, are usually enough.
  • When is it worth it? — During the learning process, when performing experiments, or in exceptionally complex business logic.

Avoid Using “or” in Selectors

  • Multilingual scenarios — If our page supports multiple languages, adding conditions like locator.getByText("Buy this book").or("Kup tę książkę") quickly becomes hard to read.
  • Better solutions:
    • Clear CSS or XPath selectors – I want to emphasize the word “clear”, because these selectors can easily become too complex. However, when kept simple, they can be very handy.
    • Data-testid — A useful approach if the team agrees to add these attributes consistently to all elements.Roles (getByRole) – in accordance with ARIA and accessibility standards.
    • Structured HTML — based on stable and unique HTML elements
  • Using text selectors can make sense when we want to test text content.
  • Visual Regression or accessibility snapshots are better ways to verify whether a particular text is visible on the page.

No static waits

  • Playwright has a built-in autowait mechanism that handles most conditions. However, there are cases where additional wait methods may be required for specific resources.  
  • What to avoid:
    • waitForTimeout — slows down operations and doesn’t resolve the real problem.
    • waitForLoadState("networkidle") — often doesn’t reflect what page readiness really means.
  • Better options:
    • Expect(locator).toBeVisible() — combines retry and auto-wait in a single method.Locator.waitFor() – when we need to wait for a specific element.
    • toPass() — useful when we need a more complex waiting mechanism.

Use Playwright MCP

  • During the test creation process, Playwright MCP can analyze the page structure and suggest stable selectors.
  • Sometimes our experience suggests a better solution, but Playwright MCP can still speed up our work and provide fresh ideas.
  • It’s worth using Playwright MCP as a helper – it may not always provide the best solution, but in many cases it’s very useful.

Stable Selector

  • Avoid indexes (first(), last()) — Today we may have one product, tomorrow ten, and our tests will fail.
  • Think in terms of the business domain — a unique method name is more stable than “ClickFirstButtonPlus”
  • Create generic selectors independent of the number of elements, they should work in all conditions.

Assertions in the test code

  • Single Responsibility Principle:
    • The Page Object should contain locators and business actions.
  • Don’t mix responsibilities — Page Objects shouldn’t contain assertions like verifyBuyButton(). It’s often unclear what such methods verify.
  • If we need to use more complex assertions — for example, comparing multiple fields — it’s a good idea to create a separate class with these methods, but the method calls should still be made in the test.

Dynamic waits for resources. Playwright doesn’t have a “magic” waitUntilEverythingIsReady().

  • The best approach:
    • Wait for specific elements — (expect(locator)...),
    • Wait for API responses (waitForResponse()),
  • It’s better to define what page readiness means for a given page instead of relying on generic methods.

Summary – useful practices in Playwright

  1. Keep selectors beyond the constructor  — clearer code and better initialization
  2. Avoid methods like clickXxx() — only use them when they add real value
  3. Use comments only when necessary —Think twice before adding one: if our code is clear, does it really need a comment?
  4. Don’t rely on text or “OR” methods — stable selectors are better than text-based ones. Text can be used in assertions to verify expected content or if the page supports only one language version. However, in most cases, it’s better to rely on stable, not text-based locators.
  5. Zero Use of Static Waits — rely on auto-waits and retryable assertions.
  6. Use assertions in the test code — page object has locators and actions. The test contains assertions.

After refactoring, the code looks better. However, I stumbled upon a problem: the test doesn’t include dynamic waits for resources that should be visible.

I added additional prompts to properly wait for elements and avoid relying on text like “buy this book” or in Polish “kup tę książkę”.

How does the code look now?

Playwright MCP one of these tests
Playwright MCP Refactored test

Generated page object – ExploreBooksPage class

Playwright MCP generated page

The code looks cleaner after refactoring. When working with Playwright MCP, you need to be mindful of potential pitfalls and learn how to avoid them. It’s worth remembering that we can almost always improve something—but in some cases, it’s better to accept the current quality as “good enough” for the context. That said, I still see areas for improvement. For example, the getFirstBookTitle method contains an assertion using expect.toHaveText(). In this case, it behaves more like an auto-wait, since toHaveText() it is an auto-retrying assertion that stabilizes the test.

Execution of these tests

After applying all improvements, the tests pass successfully.

Playwright MCP passed tests

Summary

In today’s post, I shared a few ideas for using Playwright MCP. These included working with Shadow DOM and generating Page Objects for our pages. I also highlighted how important solid technical knowledge is, as it allows you to create tests that are stable and easy to maintain.

Lack of this knowledge can lead to tests not working at all, or even if they do, being very susceptible to the slightest changes in the site structure.

The ideas for using Playwright MCP and LLM are interesting, but it’s worth remembering:

  • Verifying the effects of our experiments – not everything that is generated by LLM will be optimal or worth using.
  • The quality of prompts is crucial – a well-prepared prompt is the first step to better and more maintainable code.

Author

Michał Ślęzak

Michał Ślęzak posiada ponad 10 lat doświadczenia w automatyzacji testów, prowadząc projekty dla wiodących banków oraz współtworząc inicjatywy w branży lotniczej i innych kluczowych sektorach. Jego doświadczenie obejmuje zarówno duże korporacje, jak i innowacyjne startupy, takie jak ArtistGrowth czy WhatClinic.com. Obecnie, jako Architekt Testów w Sii, Michał odgrywa kluczową rolę w różnorodnych projektach i inicjatywach. Dzieli się także swoją wiedzą na blogu testingplus.me, poświęconym automatyzacji testów w języku polskim, oraz aktywnie uczestniczy w społeczności testerskiej, prowadząc szkolenia i występując na renomowanych konferencjach, takich jak QA Global Summit, 4Developers, TestWarez, TestFest, ConSelenium czy TestCamp.

Leave a Comment