2023-06-28 08:07:14 +00:00
# Puppeteer Angular Schematic
Adds Puppeteer-based e2e tests to your Angular project.
## Getting started
Run the command below in an Angular CLI app directory and follow the prompts.
> Note this will add the schematic as a dependency to your project.
```bash
ng add @puppeteer/ng -schematics
```
Or you can use the same command followed by the [options ](#options ) below.
2023-08-18 09:09:26 +00:00
Currently, this schematic supports the following test runners:
2023-06-28 08:07:14 +00:00
- [**Jasmine** ](https://jasmine.github.io/ )
- [**Jest** ](https://jestjs.io/ )
- [**Mocha** ](https://mochajs.org/ )
2023-07-19 07:16:17 +00:00
- [**Node Test Runner** ](https://nodejs.org/api/test.html )
2023-06-28 08:07:14 +00:00
With the schematics installed you can run E2E tests:
```bash
ng e2e
```
### Options
When adding schematics to your project you can to provide following options:
2023-12-05 12:57:09 +00:00
| Option | Description | Value | Required |
| --------------- | ------------------------------------------------------ | ------------------------------------------ | -------- |
| `--test-runner` | The testing framework to install along side Puppeteer. | `"jasmine"` , `"jest"` , `"mocha"` , `"node"` | `true` |
2023-06-28 08:07:14 +00:00
## Creating a single test file
Puppeteer Angular Schematic exposes a method to create a single test file.
```bash
2023-12-13 12:32:17 +00:00
ng generate @puppeteer/ng -schematics:e2e "< TestName > "
2023-06-28 08:07:14 +00:00
```
### Running test server and dev server at the same time
By default the E2E test will run the app on the same port as `ng start` .
To avoid this you can specify the port the an the `angular.json`
Update either `e2e` or `puppeteer` (depending on the initial setup) to:
```json
{
"e2e": {
"builder": "@puppeteer/ng-schematics:puppeteer",
"options": {
"commands": [...],
"devServerTarget": "sandbox:serve",
2023-08-18 09:09:26 +00:00
"testRunner": "< TestRunner > ",
2023-06-28 08:07:14 +00:00
"port": 8080
},
...
}
```
Now update the E2E test file `utils.ts` baseUrl to:
```ts
const baseUrl = 'http://localhost:8080';
```
## Contributing
Check out our [contributing guide ](https://pptr.dev/contributing ) to get an overview of what you need to develop in the Puppeteer repo.
2023-12-13 12:32:17 +00:00
### Sandbox smoke tests
2023-06-28 08:07:14 +00:00
2023-12-13 12:32:17 +00:00
To make integration easier smoke test can be run with a single command, that will create a fresh install of Angular (single application and a milti application projects). Then it will install the schematics inside them and run the initial e2e tests:
2023-06-28 08:07:14 +00:00
```bash
2023-12-13 12:32:17 +00:00
node tools/smoke.mjs
2023-08-03 11:02:25 +00:00
```
2023-06-28 08:07:14 +00:00
### Unit Testing
The schematics utilize `@angular-devkit/schematics/testing` for verifying correct file creation and `package.json` updates. To execute the test suit:
2024-04-11 15:47:14 +00:00
```bash npm2yarn
2023-06-28 08:07:14 +00:00
npm run test
```
2023-08-16 16:58:32 +00:00
## Migrating from Protractor
2023-12-13 12:32:17 +00:00
### Entry point
2023-08-16 16:58:32 +00:00
2023-12-13 12:32:17 +00:00
Puppeteer has its own [`browser` ](https://pptr.dev/api/puppeteer.browser ) that exposes the browser process.
A more closes comparison for Protractor's `browser` would be Puppeteer's [`page` ](https://pptr.dev/api/puppeteer.page ).
2023-08-16 16:58:32 +00:00
```ts
2023-12-13 12:32:17 +00:00
// Testing framework specific imports
import {setupBrowserHooks, getBrowserState} from './utils';
describe('< Test Name > ', function () {
setupBrowserHooks();
it('is running', async function () {
const {page} = getBrowserState();
// Query elements
await page
.locator('my-component')
// Click on the element once found
.click();
});
});
```
2023-08-16 16:58:32 +00:00
2023-12-13 12:32:17 +00:00
### Getting element properties
2023-08-16 16:58:32 +00:00
2023-12-13 12:32:17 +00:00
You can easily get any property of the element.
2023-08-16 16:58:32 +00:00
2023-12-13 12:32:17 +00:00
```ts
// Testing framework specific imports
import {setupBrowserHooks, getBrowserState} from './utils';
describe('< Test Name > ', function () {
setupBrowserHooks();
it('is running', async function () {
const {page} = getBrowserState();
2023-08-16 16:58:32 +00:00
// Query elements
2023-12-13 12:32:17 +00:00
const elementText = await page
.locator('.my-component')
.map(button => button.innerText)
// Wait for element to show up
.wait();
2023-08-16 16:58:32 +00:00
2023-12-13 12:32:17 +00:00
// Assert via assertion library
2023-08-16 16:58:32 +00:00
});
2023-12-13 12:32:17 +00:00
});
2023-08-16 16:58:32 +00:00
```
### Query Selectors
Puppeteer supports multiple types of selectors, namely, the CSS, ARIA, text, XPath and pierce selectors.
The following table shows Puppeteer's equivalents to [Protractor By ](https://www.protractortest.org/#/api?view=ProtractorBy ).
> For improved reliability and reduced flakiness try our
> **Experimental** [Locators API](https://pptr.dev/guides/locators)
2023-08-23 12:38:12 +00:00
| By | Protractor code | Puppeteer querySelector |
| ----------------- | --------------------------------------------- | ------------------------------------------------------------ |
| CSS (Single) | `$(by.css('<CSS>'))` | `page.$('<CSS>')` |
| CSS (Multiple) | `$$(by.css('<CSS>'))` | `page.$$('<CSS>')` |
| Id | `$(by.id('<ID>'))` | `page.$('#<ID>')` |
| CssContainingText | `$(by.cssContainingText('<CSS>', '<TEXT>'))` | `page.$('<CSS> ::-p-text(<TEXT>)')` ` |
| DeepCss | `$(by.deepCss('<CSS>'))` | `page.$(':scope >>> <CSS>')` |
| XPath | `$(by.xpath('<XPATH>'))` | `page.$('::-p-xpath(<XPATH>)')` |
| JS | `$(by.js('document.querySelector("<CSS>")'))` | `page.evaluateHandle(() => document.querySelector('<CSS>'))` |
2023-08-16 16:58:32 +00:00
> For advanced use cases such as Protractor's `by.addLocator` you can check Puppeteer's [Custom selectors](https://pptr.dev/guides/query-selectors#custom-selectors).
2023-12-13 12:32:17 +00:00
### Actions Selectors
Puppeteer allows you to all necessary actions to allow test your application.
```ts
// Click on the element.
element(locator).click();
// Puppeteer equivalent
await page.locator(locator).click();
// Send keys to the element (usually an input).
element(locator).sendKeys('my text');
// Puppeteer equivalent
await page.locator(locator).fill('my text');
// Clear the text in an element (usually an input).
element(locator).clear();
// Puppeteer equivalent
await page.locator(locator).fill('');
// Get the value of an attribute, for example, get the value of an input.
element(locator).getAttribute('value');
// Puppeteer equivalent
const element = await page.locator(locator).waitHandle();
const value = await element.getProperty('value');
```
### Example
Sample Protractor test:
```ts
describe('Protractor Demo', function () {
it('should add one and two', function () {
browser.get('http://juliemr.github.io/protractor-demo/');
element(by.model('first')).sendKeys(1);
element(by.model('second')).sendKeys(2);
element(by.id('gobutton')).click();
expect(element(by.binding('latest')).getText()).toEqual('3');
});
});
```
Sample Puppeteer migration:
```ts
import {setupBrowserHooks, getBrowserState} from './utils';
describe('Puppeteer Demo', function () {
setupBrowserHooks();
it('should add one and two', function () {
const {page} = getBrowserState();
await page.goto('http://juliemr.github.io/protractor-demo/');
await page.locator('.form-inline > input:nth-child(1)').fill('1');
await page.locator('.form-inline > input:nth-child(2)').fill('2');
await page.locator('#gobutton').fill('2');
const result = await page
.locator('.table tbody td:last-of-type')
.map(header => header.innerText)
.wait();
expect(result).toEqual('3');
});
});
```