zombieplus GitHub Details, Stars and Alternatives | OpenRepoFinder
andrelkj / repository
zombieplus
Project to automate Zombie+ (a copy of Disney+ platform) using playwright framework to write tests, postgres SQL and Docker to store and mantain data. REST API for backend validation, and github actions as pipeline for continuos integration.
A transparent discovery signal based on current public GitHub metadata.
Recent activity35% weight
10
Community adoption25% weight
0
Maintenance state20% weight
100
License clarity10% weight
0
Project information10% weight
75
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
README preview
Playwright
🤘 About
Repository for the Zombie Plus system's automated test project, built during the Playwright Zombie Edition course! Playwright is an open-source tool developed by Microsoft that revolutionizes test automation in web systems, offering an effective and highly reliable approach.
💻 Technologies
Node.js
Playwright
Javascript
Faker
PostgreSQL
🤖 How to run
Clone the repository, install dependencies with npm install
Run tests in Headless mode with npx playwright test
View the test report with npx playwright show-report
📑 Documents
For this project two main documents were created:
Zombie+ Roadmap - that includes test cases for each application functionality
We are using Postgres SQL as the main database that is running locally through Docker containers. In order to stablish connection to the postgres database we installed the pg library npm i pg --save-dev.
📨 API
To manage requests from Zombie+ application we are using Insomnia.
⚙️ Commands
Running tests
To run playwright tests we need to run npx playwright test.
Note: playwright standard is to run tests in headless mode, you can run the assisted execution by adding --headed statement.
Some common statements are:
--headed to run the test in assisted mode
debug to run the test execution step by step in debbuger mode
ui to open playwright gui page
💡 Tricks
Temporary elements
To work with temporary components like toasts you can set a checkpoint or timeout to wait for the toast,
await page.getByText('seus dados conosco').click();
use playwright ui npx playwright test --ui to look for it, and then use the following code to get page html content and look for the complete element you need.
ALGORITHMICALLY RELATED
Similar Open-Source Projects
Selected from shared topics, language and repository description—not editorial ratings.
Project to automate Samurai Barber Shop using cypress framework to write tests, postgreSQL and ElephantSQL to store and maintain data. REST API for backend validation, and github actions as pipeline for continuos integration.
// handling toast and temporary elements
const content = await page.content();
console.log(content);
Note: it might be required when working with temporary elements that are hard to interact with.
Validation of elements with same locator
Playwright handles validations against the same element locator or multielement locators as a forEach loop. It means that you can use arrays to declare what is expected and playwright will run it as a loop for each locator found to the given selector:
// working with multielement locators
await expect(page.locator('.alert')).toHaveText([
'Campo obrigatório',
'Campo obrigatório',
]);
Regex
It is possible to use regex with locators and arguments or expected messages to create a more dynamic and reusable test structure.
Using regex to get only a piece of the url:
async isLoggedIn() {
await this.page.waitForLoadState('networkidle'); // wait all network traffic is finished
await expect(this.page).toHaveURL(/.*admin/);
}
Using CSS selectors with regex span[class$=alert] to simplify element search:
Javascript standard convension is to use camelCase model, although we are using snake_case notation in the MoviesPage just to match this project database and REST API notation which can provide a better data handling and management.
Elements relation by label
in HTML when there is a label for using the same value as the id of a child element it stablish a connection in between these elements:
Database connection for reliable and reusable test data
When working with tests data require data injection, a good practice is to stablish connections with the available database to manage the data available easly.
By using this approach you can generate independent, reusable, fresh data on every test execution, avoiding:
storage space consumption
flaky tests caused by mutable shared data
large loads of data to handle
To connect with the database we first need to provide the credentials:
const DbConfig = {
user: 'username', // username to access your
host: 'localhost', // since where running locally
database: 'dbname', // db name where you want to run the query against
password: 'password', // password to access your database
port: 5432,
};
Note: you can get this information into your database properties.
then you stablesh the connection, handling errors with:
export async function executeSQL(sqlScript) {
const pool = new Pool(DbConfig);
try {
const client = await pool.connect();
const result = await client.query(sqlScript);
console.log(result.rows);
} catch (error) {
console.log('Erro ao executar o SQL ' + error);
}
}
Centralize Page Object Model (POM) imports
When using POM it is required to import each page context so you can actually use their own functions, as the application grows this imports can get out of hand though and represent a lot of your code lines.
One way to handle it is by defining a index.js file in the support folder that will empower the existent page context from playwright with all of your imports:
const { test: base } = require('@playwright/test');
const { LandingPage } = require('../pages/LandingPage');
const { LoginPage } = require('../pages/LoginPage');
const { MoviesPage } = require('../pages/MoviesPage');
const { Toast } = require('../pages/Components');
// create a updated page context that extends the actual page plus all POM imports injected
const test = base.extend({
page: async ({ page }, use) => {
await use({
...page,
landing: new LandingPage(page),
login: new LoginPage(page),
movies: new MoviesPage(page),
toast: new Toast(page),
});
},
});
export { test };
after that you can replace the test import from playwright to your new context inside your test file:
// standard test import from playwright
const { test: base } = require('@playwright/test');
// new import from index.js
const { test } = require('../support');
Note: Javascript understands index.js files as the main ones, it means that even though you don't expecify it whitin the import this file will be use.
at the end you just need to update your test case to use the new format and remove all the old imports:
const { test } = require('../support');
const data = require('../support/fixtures/movies.json');
const { executeSQL } = require('../support/database');
test('deve poder cadastrar um novo filme', async ({ page }) => {
// é importante estar logado
const movie = data.create;
await executeSQL(`DELETE FROM public.movies WHERE title = '${movie.title}';`);
await page.login.visit();
await page.login.submit('admin@zombieplus.com', 'pwd123');
await page.movies.isLoggedIn();
await page.movies.create(
movie.title,
movie.overview,
movie.company,
movie.release_year
);
await page.toast.containText('Cadastro realizado com sucesso!');
});
Note: although it helps to centralize all import into a single file you need to consider it's impact into the execution performance once you'll load the context of all pages everytime instead of only the page specific contexts as before.
Update to import standard functions from page context
The approach used above to inject can be applied to centralize POM imports, but it causes standard playwright functions that comes with the page context to be lost.
In order to fix that we updated the index.js file to store the original page content into a variable and then injecting each individual page initialization to it:
const test = base.extend({
page: async ({ page }, use) => {
// store the original page context into a variable
const context = page;
// inject each individual page into the page context
context['landing'] = new LandingPage(page);
context['login'] = new LoginPage(page);
context['movies'] = new MoviesPage(page);
context['toast'] = new Toast(page);
await use(context);
},
});
Note: at this point you should be able to use both native playwright and page specific functions.
Parallel execution issues
Playwright executes test cases and test suites in parallel by default:
although when working with requests to the API of beforeAll hooks, this parallelization of execution can cause issues because this beforeAll will be called more than 1 time (it turns into a beforeEach basically), e.g. public.movie delete query that deletes movies in the middle of the test execution.
to fix that we can define playwright config filefullyParallel: false, which will still run suites in multitread, but will execute each individual test case at a time.
Define specific deletions
Another alternative to keep test cases running in parallel would be to set one deletion query for each test case that would delete test case specific movies by title:
test('deve poder cadastrar um novo filme', async ({ page }) => {
const movie = data.create;
await executeSQL(`DELETE FROM public.movies WHERE title='${movie.title}'`);
await page.login.do('admin@zombieplus.com', 'pwd123', 'Admin');
await page.movies.create(movie);
await page.popup.haveText(
`O filme '${movie.title}' foi adicionado ao catálogo.`
);
});
✅ Best practices
Test independence
Playwright is build in a way that all test cases are executed simultaneasly so dependent test cases often fail once there is no assurance that they'll be executed in the same order every time.
With that said dependent test cases as those two here:
// register the movie
test('deve poder cadastrar um novo filme', async ({ page }) => {
const movie = data.create;
await executeSQL(`DELETE FROM public.movies WHERE title = '${movie.title}';`);
await page.login.do('admin@zombieplus.com', 'pwd123', 'Admin');
await page.movies.create(movie);
await page.toast.containText('Cadastro realizado com sucesso!');
});
// try to register the same movie again
test('não deve cadastrar quando o título é duplicado', async ({ page }) => {
const movie = data.create;
await page.login.do('admin@zombieplus.com', 'pwd123', 'Admin');
await page.movies.create(movie);
await page.toast.containText(
'Este conteúdo já encontra-se cadastrado no catálogo'
);
});
Are mostly likely going to fail once the duplicated scenario can run first and cause to movie to be successfully registered and then the movie registration scenarion runs secondly receiving the duplicated error message.
Note: other frameworks may allow you to run tests sequentially making it work, but it still a bad practice in automation as both tests might fail in case something happens in the process.
Page Object Model (POM)
When using POM it is import to keep the rules and folder structure