Oral  Brekke

Oral Brekke

1655970960

Jest-playwright: Running tests using Jest & Playwright

Jest Playwright

It's more flexible, lightweight, optimized for Playwright, and has TypeScript support out of the box. This doesn't mean, that we stop with maintaining this package.


Running your tests using Jest & Playwright

npm install -D jest jest-playwright-preset playwright

Also you can use jest-playwright-preset with specific playwright packages: playwright-webkit, playwright-chromium and playwright-firefox

npm install -D jest jest-playwright-preset playwright-firefox

Requirements

  • Node.js >= 10.15.0
  • Playwright >= 0.12.1
  • Jest >= 25

Usage

Update your Jest configuration, either:

with package.json:

"jest": {
  "preset": "jest-playwright-preset"
}

or with jest.config.js:

module.exports = {
  preset: 'jest-playwright-preset',
}

And add the Jest command as in the script section of your package.json:

{
  "scripts": {
    "test": "jest"
  }
}

Now you can use Playwright in your tests:

beforeAll(async () => {
  await page.goto('https://whatismybrowser.com/')
})

test('should display correct browser', async () => {
  const browser = await page.$eval('.string-major', (el) => el.innerHTML)
  expect(browser).toContain('Chrome')
})

Notes

playwright actions can take some time for execution, because of it jest-playwright overrides jest default timeout interval from 5 to 15 seconds. You can change this interval with testTimeout in your jest configuration.

Configuration

It's recommend to use a separate Jest configuration jest.e2e.config.js for jest-playwright to gain speed improvements and by that to only use Playwright in the end-to-end tests. For that you have to use the -c flag when calling Jest and use the testMatch or testRegex in your Jest config to split them.

Be sure to remove any existing testEnvironment option from your Jest configuration. The jest-playwright-preset preset needs to manage that option itself.

Configuration options can be specified using a jest-playwright.config.js file at the root of your project:

// jest-playwright.config.js

module.exports = {
  // Options...
}

Similar to Jest globalSetup configuration can except the export of an async function:

module.exports = async () => {
  await ...
};

A custom path can be specified to the jest-playwright.config.js file within your jest.config.js file:

process.env.JEST_PLAYWRIGHT_CONFIG = '/path/to/jest-playwright.config.js'

Alternatively, configuration options can specified using Jest's own testEnvironmentOptions option within your jest.config.js file:

// jest.config.js

module.exports = {
  preset: 'jest-playwright-preset',
  testEnvironmentOptions: {
    'jest-playwright': {
      // Options...
    },
  },
}

Options

  • launchOptions <[object]>. All Playwright launch options can be specified in config. Since it is JavaScript, you can use all stuff you need, including environment.
  • launchType <LAUNCH | PERSISTENT | SERVER>. Method to launch browser instance. jest-playwright attaches Playwright to an existing browser instance by default.
  • connectOptions <[object]>. All Playwright connect options can be specified in config.
  • contextOptions <[object]>. All Playwright context options can be specified in config.
  • browsers <[(string | object)[]]>. Define browsers to run tests in.
    • chromium Each test runs Chromium (default).
    • firefox Each test runs Firefox.
    • webkit Each test runs Webkit.
  • devices <[(string | object)[] | RegExp]>. Define a devices to run tests in. Actual list of devices can be found here.
  • exitOnPageError <[boolean]>. Exits process on any page error. Defaults to true.
  • collectCoverage <[boolean]>. Enables the coverage collection of the saveCoverage(page) calls to the .nyc_output/coverage.json file.
  • serverOptions <[object]>. All jest-process-manager options.
  • selectors <[array]>. Define selectors. Each selector must be an object with name and script properties.
  • skipInitialization <[boolean]>. Add you ability to skip first setup playwright process. Possible use cases can be found here
  • resetContextPerTest <[boolean]>. Option for opening a new context per test
  • useDefaultBrowserType <[boolean]>. Sometimes browser + device combinations don't have any sense. With this option tests will be run with defaultBrowserType of device. Pay attention that you should define devices to correct usage of this option.

Usage of process environment to define browser

You can control the browser with passing environment variable.

// jest-playwright.config.js
module.exports = {
  browsers: [process.env.BROWSER],
}

Specific browser options

For launchOptions, connectOptions and contextOptions you can define special browser options.

// jest-playwright.config.js
module.exports = {
  connectOptions: {
    chromium: {
      wsEndpoint: 'ws://chrome.proxy.com:4444'
    },
    firefox: {
      wsEndpoint: 'ws://firefox.proxy.com:4444'
    }
  },
  ...
}

Browser configuration

There are different ways to define devices in your configuration file:

  • You can use array of browser names:
module.exports = {
  browsers: ["chromium", "webkit"],
  ...
}
  • You can define custom browser. You can find out use cases here:
{
  // Name of browser
  name: 'chromium' | 'firefox' | 'webkit'
  // Display name for test
  displayName: string
  ...
  // Browser options
}

Device configuration

There are different ways to define devices in your configuration file:

  • You can use array of device names:
module.exports = {
  devices: ["iPhone 6", "Pixel 2"],
  ...
}
  • You can use RegExp:
module.exports = {
  devices: /iPhone 8/,
  ...
}
  • Also you can define custom device:
{
  // Name of device
  name: string
  // Page width and height
  viewport: {
    width: number
    height: number
  }
  // user agent
  userAgent: string
  // device scale factor
  deviceScaleFactor: number
  // is device is mobile
  isMobile: boolean
  // support of touch events
  hasTouch: boolean
  // device default browser
  defaultBrowserType: chromium, firefox or webkit
}

Globals

  • browserName <[string]> - name of the current browser (chromium, firefox or webkit)
  • deviceName <[string]> - name of the current device
  • browser <[Browser]> - Playwright browser instance
  • context <[Context]> - a new Playwright context instance for each new test file
  • page <[Page]> - Playwright page instance (since a new context for every test file also creates a new page for it)

All of them are available globally in each Jest test. If you are using ESLint and JavaScript, its recommend to use it in combination with the eslint-plugin-jest-playwright.

Debug mode

Playwright give you ability to configure the browser for debugging with the PWDEBUG environment variable. It will launch the browser in headful mode, disables playwright timeout and Jest won't timeout anymore.:

PWDEBUG=1 jest

Reset helper functions

Reset current page

beforeEach(async () => {
  await jestPlaywright.resetPage()
})

To create a new page for each test, you can use this snippet to have a new page object for each individual test.

Reset current context

beforeEach(async () => {
  await jestPlaywright.resetContext()
})

To create a new context for each test, you can use this snippet to have a new context object for each individual test.

Reset current browser

beforeEach(async () => {
  await jestPlaywright.resetBrowser()
})

You can use this snippet to reset current browser for each individual test. It will reset browser, context and page.

Debug helper functions

jest-playwright provides some functions to debug your tests.

IMPORTANT NOTE: For these kind of tests you should use properties passed through callback function instead of globals

jestPlaywrightDebug

This helper function provide you ability to run specific tests in debug mode. It will disable headless mode. You can find more information here

test.jestPlaywrightDebug('failed', async ({ page }) => {
  await page.goto('https://github.com/')
  const title = await page.title()
  await expect(title).toBe('Google')
})

Also you can define options for debug mode with debugOptions:

// jest-playwright.config.js
module.exports = {
  debugOptions: {
    ...
    contextOptions: {
      offline: true
    }
  }
  ...
}

jestPlaywrightConfig

This helper function provide you ability to run specific tests with passed options. You can define browser and device properties to run test for them, otherwise test run for current configuration.

test.jestPlaywrightConfig(
  {
    // your jest-playwright options
  },
  'test name',
  async ({ browser, context, page }) => {
    /* ... */
  },
)

Tracking the coverage

It's possible to track the coverage of the end-to-end tests with the babel-plugin-istanbul Babel plugin configured. It needs to be included in the web application which you are gonna test otherwise it won't work. To use it, you have to set collectCoverage in the jest-playwright.config.js to true. Per default the test coverage will be automatically saved after each navigation change (beforeunload event). If a certain code path is not covered, you can manually call and add the corresponding saveCoverage(page) call to your tests like that:

await jestPlaywright.saveCoverage(page)

By using coverage collection, it will write the coverage data to the .nyc_output/coverage.json file which can be transformed using nyc to the lcov format:

npx nyc report --reporter=lcovonly

or to HTML:

npx nyc report --reporter=html

which will create a HTML website in the coverage directory.

Skip tests for specific browsers and devices

It's possible to skip tests for browsers or combination of browsers and devices

it.jestPlaywrightSkip(
  { browsers: ['chromium'] },
  'should skip this one',
  async () => {
    const title = await page.title()
    expect(title).toBe('Google')
  },
)

Using shadow DOM selectors

Playwright engine pierces open shadow DOM by default.

beforeAll(async () => {
  await page.goto(
    'https://mdn.github.io/web-components-examples/popup-info-box-web-component/',
  )
})

test('should display "google" text on page', async () => {
  const shadowElem = await page.$('.info')
  const shadowElemText = await shadowElem.innerHTML()

  expect(shadowElemText).toBe(
    'Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card.',
  )
})

Start a server

Jest Playwright integrates a functionality to start a server when running your test suite, like jest-puppeteer. It automatically closes the server when tests are done.

To use it, specify a server section in your jest-playwright.config.js.

// jest-playwright.config.js
module.exports = {
  serverOptions: {
    command: 'node server.js',
    port: 4444,
  },
}

Other options are documented in jest-process-manager.

Using with different jest environments

The default jest-playwright environment is node, but you can use a browser-like environment through jest-playwright-jsdom

expect-playwright

There is a utility package expect-playwright which simplifies the expect statements in combination with Playwright to make e.g. shorter text comparisons.

ESLint globals / 'page' is not defined

There is an ESLint plugin available eslint-plugin-jest-playwright available which includes the globals for using jest-playwright.

Unstable and experimental API

You can run tests for multiple browsers and devices:

  • You must have installed the playwright package
  • You must define browsers to test with your jest-playwright.config.js:
module.exports = {
    browsers: ["chromium", "webkit"],
    devices: ["iPhone 6", "Pixel 2"],
    ...
}

It will run your tests for:

  • Chromium browser and iPhone 6 device;
  • Chromium browser and Pixel 2 device;
  • Webkit browser and iPhone 6 device;
  • Webkit browser and Pixel 2 device;

If there is no defined browsers in config it will run tests for chromium browser.

Usage with custom testEnvironment

You can use jest-playwright with custom test environment for taking screenshots during test failures for example:

jest.config.json

"testEnvironment": "./CustomEnvironment.js"

CustomEnvironment.js

const PlaywrightEnvironment = require('jest-playwright-preset/lib/PlaywrightEnvironment')
  .default

class CustomEnvironment extends PlaywrightEnvironment {
  async setup() {
    await super.setup()
    // Your setup
  }

  async teardown() {
    // Your teardown
    await super.teardown()
  }

  async handleTestEvent(event) {
    await super.handleTestEvent(event);
    if (event.name === 'test_done' && event.test.errors.length > 0) {
      const parentName = event.test.parent.name.replace(/\W/g, '-')
      const specName = event.test.name.replace(/\W/g, '-')

      await this.global.page.screenshot({
        path: `screenshots/${parentName}_${specName}.png`,
      })
    }
  }
}

module.exports = CustomEnvironment

Usage with custom runner

jest-playwright using custom runner underhood. So if you need implement your own runner, you should extend it:

jest.config.json

"runner": "./CustomRunner.js"

CustomRunner.js

const PlaywrightRunner = require('jest-playwright-preset/lib/PlaywrightRunner')
  .default

class CustomRunner extends PlaywrightRunner {
  constructor(...args) {
    super(...args)
    this.isSerial = true
  }
}

module.exports = CustomRunner

Usage with custom globalSetup and globalTeardown

For this use case, jest-playwright-preset exposes two methods: globalSetup and globalTeardown, so that you can wrap them with your own global setup and global teardown methods as the following example:

Getting authentication state once for all test cases as per playwright reference:

// global-setup.js
import { globalSetup as playwrightGlobalSetup } from 'jest-playwright-preset';

module.exports = async function globalSetup(globalConfig) {
  await playwrightGlobalSetup(globalConfig);

  const browserServer = await chromium.launchServer();
  const wsEndpoint = browserServer.wsEndpoint();
  const browser = await chromium.connect({ wsEndpoint: wsEndpoint });
  const page = await browser.newPage();

  // your login function
  await doLogin(page);

  // store authentication data
  const storage = await page.context().storageState();
  process.env.STORAGE = JSON.stringify(storage);
};
// global-teardown.js
import { globalTeardown as playwrightGlobalTeardown } from 'jest-playwright-preset';

module.exports = async function globalTeardown(globalConfig) {
  // Your global teardown
  await playwrightGlobalTeardown(globalConfig);
}

Then assigning your js file paths to the globalSetup and globalTeardown property in your Jest configuration.

{
  // ...
  "globalSetup": "./global-setup.js",
  "globalTeardown": "./global-teardown.js"
}

Now your custom globalSetup and globalTeardown will be triggered once before and after all test suites.

Usage with Typescript

Example Jest configuration in combination with ts-jest:

module.exports = {
  preset: 'jest-playwright-preset',
  transform: {
    '^.+\\.ts$': 'ts-jest',
  },
}

Types are also available, which you can either use via directly in your test:

/// <reference types="jest-playwright-preset" />
/// <reference types="expect-playwright" />

or at your central tsconfig.json either via files:

{
  "files": [
    "./global.d.ts",
    "node_modules/jest-playwright-preset/types/global.d.ts",
    "node_modules/expect-playwright/global.d.ts"
  ]
}

or via types:

{
  "compilerOptions": {
    "types": ["jest-playwright-preset", "expect-playwright"]
  }
}

It's important to not change the testEnvironment to node. Otherwise it won't work.

Known issues

Error reporting with Jest

If you face into error messages like

UnhandledPromiseRejectionWarning: Error: Protocol error (Runtime.callFunctionOn): Target closed.

or

Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Error:

and your Jest error reporting will only show that an entire test (it() function) has failed, then you need to increase the Jest timeout because the Playwright timeout is greater than the Jest timeout. So Jest in the end will simply stop the execution and no verbose (which exact line) error reporting can be generated.

To fix this behavior simply call

jest.setTimeout(35 * 1000)

in your tests at the top. (30 seconds is the default Playwright timeout for waiting for an specific element.)

New Browser instance for each test

If for your individual tests a new entire browser instance spins up each time and it won't be reused, then you probably run them in parallel. If you run them in a synchronous way with the --runInBand CLI option for Jest, then the same browser instance will be re-used and this should fix the issue.

Examples

Demonstration the usage of jest-playwright for various test cases can be found in playwright-jest-examples

Inspiration

Thanks to Smooth Code for the great jest-puppeteer.

⚠️ We recommend the official Playwright test-runner (@playwright/test) ⚠️

Author: Playwright-community
Source Code: https://github.com/playwright-community/jest-playwright/ 
License: MIT license

#node #jest #playwright #testing #typescript 

What is GEEK

Buddha Community

Jest-playwright: Running tests using Jest & Playwright
Chloe  Butler

Chloe Butler

1667425440

Pdf2gerb: Perl Script Converts PDF Files to Gerber format

pdf2gerb

Perl script converts PDF files to Gerber format

Pdf2Gerb generates Gerber 274X photoplotting and Excellon drill files from PDFs of a PCB. Up to three PDFs are used: the top copper layer, the bottom copper layer (for 2-sided PCBs), and an optional silk screen layer. The PDFs can be created directly from any PDF drawing software, or a PDF print driver can be used to capture the Print output if the drawing software does not directly support output to PDF.

The general workflow is as follows:

  1. Design the PCB using your favorite CAD or drawing software.
  2. Print the top and bottom copper and top silk screen layers to a PDF file.
  3. Run Pdf2Gerb on the PDFs to create Gerber and Excellon files.
  4. Use a Gerber viewer to double-check the output against the original PCB design.
  5. Make adjustments as needed.
  6. Submit the files to a PCB manufacturer.

Please note that Pdf2Gerb does NOT perform DRC (Design Rule Checks), as these will vary according to individual PCB manufacturer conventions and capabilities. Also note that Pdf2Gerb is not perfect, so the output files must always be checked before submitting them. As of version 1.6, Pdf2Gerb supports most PCB elements, such as round and square pads, round holes, traces, SMD pads, ground planes, no-fill areas, and panelization. However, because it interprets the graphical output of a Print function, there are limitations in what it can recognize (or there may be bugs).

See docs/Pdf2Gerb.pdf for install/setup, config, usage, and other info.


pdf2gerb_cfg.pm

#Pdf2Gerb config settings:
#Put this file in same folder/directory as pdf2gerb.pl itself (global settings),
#or copy to another folder/directory with PDFs if you want PCB-specific settings.
#There is only one user of this file, so we don't need a custom package or namespace.
#NOTE: all constants defined in here will be added to main namespace.
#package pdf2gerb_cfg;

use strict; #trap undef vars (easier debug)
use warnings; #other useful info (easier debug)


##############################################################################################
#configurable settings:
#change values here instead of in main pfg2gerb.pl file

use constant WANT_COLORS => ($^O !~ m/Win/); #ANSI colors no worky on Windows? this must be set < first DebugPrint() call

#just a little warning; set realistic expectations:
#DebugPrint("${\(CYAN)}Pdf2Gerb.pl ${\(VERSION)}, $^O O/S\n${\(YELLOW)}${\(BOLD)}${\(ITALIC)}This is EXPERIMENTAL software.  \nGerber files MAY CONTAIN ERRORS.  Please CHECK them before fabrication!${\(RESET)}", 0); #if WANT_DEBUG

use constant METRIC => FALSE; #set to TRUE for metric units (only affect final numbers in output files, not internal arithmetic)
use constant APERTURE_LIMIT => 0; #34; #max #apertures to use; generate warnings if too many apertures are used (0 to not check)
use constant DRILL_FMT => '2.4'; #'2.3'; #'2.4' is the default for PCB fab; change to '2.3' for CNC

use constant WANT_DEBUG => 0; #10; #level of debug wanted; higher == more, lower == less, 0 == none
use constant GERBER_DEBUG => 0; #level of debug to include in Gerber file; DON'T USE FOR FABRICATION
use constant WANT_STREAMS => FALSE; #TRUE; #save decompressed streams to files (for debug)
use constant WANT_ALLINPUT => FALSE; #TRUE; #save entire input stream (for debug ONLY)

#DebugPrint(sprintf("${\(CYAN)}DEBUG: stdout %d, gerber %d, want streams? %d, all input? %d, O/S: $^O, Perl: $]${\(RESET)}\n", WANT_DEBUG, GERBER_DEBUG, WANT_STREAMS, WANT_ALLINPUT), 1);
#DebugPrint(sprintf("max int = %d, min int = %d\n", MAXINT, MININT), 1); 

#define standard trace and pad sizes to reduce scaling or PDF rendering errors:
#This avoids weird aperture settings and replaces them with more standardized values.
#(I'm not sure how photoplotters handle strange sizes).
#Fewer choices here gives more accurate mapping in the final Gerber files.
#units are in inches
use constant TOOL_SIZES => #add more as desired
(
#round or square pads (> 0) and drills (< 0):
    .010, -.001,  #tiny pads for SMD; dummy drill size (too small for practical use, but needed so StandardTool will use this entry)
    .031, -.014,  #used for vias
    .041, -.020,  #smallest non-filled plated hole
    .051, -.025,
    .056, -.029,  #useful for IC pins
    .070, -.033,
    .075, -.040,  #heavier leads
#    .090, -.043,  #NOTE: 600 dpi is not high enough resolution to reliably distinguish between .043" and .046", so choose 1 of the 2 here
    .100, -.046,
    .115, -.052,
    .130, -.061,
    .140, -.067,
    .150, -.079,
    .175, -.088,
    .190, -.093,
    .200, -.100,
    .220, -.110,
    .160, -.125,  #useful for mounting holes
#some additional pad sizes without holes (repeat a previous hole size if you just want the pad size):
    .090, -.040,  #want a .090 pad option, but use dummy hole size
    .065, -.040, #.065 x .065 rect pad
    .035, -.040, #.035 x .065 rect pad
#traces:
    .001,  #too thin for real traces; use only for board outlines
    .006,  #minimum real trace width; mainly used for text
    .008,  #mainly used for mid-sized text, not traces
    .010,  #minimum recommended trace width for low-current signals
    .012,
    .015,  #moderate low-voltage current
    .020,  #heavier trace for power, ground (even if a lighter one is adequate)
    .025,
    .030,  #heavy-current traces; be careful with these ones!
    .040,
    .050,
    .060,
    .080,
    .100,
    .120,
);
#Areas larger than the values below will be filled with parallel lines:
#This cuts down on the number of aperture sizes used.
#Set to 0 to always use an aperture or drill, regardless of size.
use constant { MAX_APERTURE => max((TOOL_SIZES)) + .004, MAX_DRILL => -min((TOOL_SIZES)) + .004 }; #max aperture and drill sizes (plus a little tolerance)
#DebugPrint(sprintf("using %d standard tool sizes: %s, max aper %.3f, max drill %.3f\n", scalar((TOOL_SIZES)), join(", ", (TOOL_SIZES)), MAX_APERTURE, MAX_DRILL), 1);

#NOTE: Compare the PDF to the original CAD file to check the accuracy of the PDF rendering and parsing!
#for example, the CAD software I used generated the following circles for holes:
#CAD hole size:   parsed PDF diameter:      error:
#  .014                .016                +.002
#  .020                .02267              +.00267
#  .025                .026                +.001
#  .029                .03167              +.00267
#  .033                .036                +.003
#  .040                .04267              +.00267
#This was usually ~ .002" - .003" too big compared to the hole as displayed in the CAD software.
#To compensate for PDF rendering errors (either during CAD Print function or PDF parsing logic), adjust the values below as needed.
#units are pixels; for example, a value of 2.4 at 600 dpi = .0004 inch, 2 at 600 dpi = .0033"
use constant
{
    HOLE_ADJUST => -0.004 * 600, #-2.6, #holes seemed to be slightly oversized (by .002" - .004"), so shrink them a little
    RNDPAD_ADJUST => -0.003 * 600, #-2, #-2.4, #round pads seemed to be slightly oversized, so shrink them a little
    SQRPAD_ADJUST => +0.001 * 600, #+.5, #square pads are sometimes too small by .00067, so bump them up a little
    RECTPAD_ADJUST => 0, #(pixels) rectangular pads seem to be okay? (not tested much)
    TRACE_ADJUST => 0, #(pixels) traces seemed to be okay?
    REDUCE_TOLERANCE => .001, #(inches) allow this much variation when reducing circles and rects
};

#Also, my CAD's Print function or the PDF print driver I used was a little off for circles, so define some additional adjustment values here:
#Values are added to X/Y coordinates; units are pixels; for example, a value of 1 at 600 dpi would be ~= .002 inch
use constant
{
    CIRCLE_ADJUST_MINX => 0,
    CIRCLE_ADJUST_MINY => -0.001 * 600, #-1, #circles were a little too high, so nudge them a little lower
    CIRCLE_ADJUST_MAXX => +0.001 * 600, #+1, #circles were a little too far to the left, so nudge them a little to the right
    CIRCLE_ADJUST_MAXY => 0,
    SUBST_CIRCLE_CLIPRECT => FALSE, #generate circle and substitute for clip rects (to compensate for the way some CAD software draws circles)
    WANT_CLIPRECT => TRUE, #FALSE, #AI doesn't need clip rect at all? should be on normally?
    RECT_COMPLETION => FALSE, #TRUE, #fill in 4th side of rect when 3 sides found
};

#allow .012 clearance around pads for solder mask:
#This value effectively adjusts pad sizes in the TOOL_SIZES list above (only for solder mask layers).
use constant SOLDER_MARGIN => +.012; #units are inches

#line join/cap styles:
use constant
{
    CAP_NONE => 0, #butt (none); line is exact length
    CAP_ROUND => 1, #round cap/join; line overhangs by a semi-circle at either end
    CAP_SQUARE => 2, #square cap/join; line overhangs by a half square on either end
    CAP_OVERRIDE => FALSE, #cap style overrides drawing logic
};
    
#number of elements in each shape type:
use constant
{
    RECT_SHAPELEN => 6, #x0, y0, x1, y1, count, "rect" (start, end corners)
    LINE_SHAPELEN => 6, #x0, y0, x1, y1, count, "line" (line seg)
    CURVE_SHAPELEN => 10, #xstart, ystart, x0, y0, x1, y1, xend, yend, count, "curve" (bezier 2 points)
    CIRCLE_SHAPELEN => 5, #x, y, 5, count, "circle" (center + radius)
};
#const my %SHAPELEN =
#Readonly my %SHAPELEN =>
our %SHAPELEN =
(
    rect => RECT_SHAPELEN,
    line => LINE_SHAPELEN,
    curve => CURVE_SHAPELEN,
    circle => CIRCLE_SHAPELEN,
);

#panelization:
#This will repeat the entire body the number of times indicated along the X or Y axes (files grow accordingly).
#Display elements that overhang PCB boundary can be squashed or left as-is (typically text or other silk screen markings).
#Set "overhangs" TRUE to allow overhangs, FALSE to truncate them.
#xpad and ypad allow margins to be added around outer edge of panelized PCB.
use constant PANELIZE => {'x' => 1, 'y' => 1, 'xpad' => 0, 'ypad' => 0, 'overhangs' => TRUE}; #number of times to repeat in X and Y directions

# Set this to 1 if you need TurboCAD support.
#$turboCAD = FALSE; #is this still needed as an option?

#CIRCAD pad generation uses an appropriate aperture, then moves it (stroke) "a little" - we use this to find pads and distinguish them from PCB holes. 
use constant PAD_STROKE => 0.3; #0.0005 * 600; #units are pixels
#convert very short traces to pads or holes:
use constant TRACE_MINLEN => .001; #units are inches
#use constant ALWAYS_XY => TRUE; #FALSE; #force XY even if X or Y doesn't change; NOTE: needs to be TRUE for all pads to show in FlatCAM and ViewPlot
use constant REMOVE_POLARITY => FALSE; #TRUE; #set to remove subtractive (negative) polarity; NOTE: must be FALSE for ground planes

#PDF uses "points", each point = 1/72 inch
#combined with a PDF scale factor of .12, this gives 600 dpi resolution (1/72 * .12 = 600 dpi)
use constant INCHES_PER_POINT => 1/72; #0.0138888889; #multiply point-size by this to get inches

# The precision used when computing a bezier curve. Higher numbers are more precise but slower (and generate larger files).
#$bezierPrecision = 100;
use constant BEZIER_PRECISION => 36; #100; #use const; reduced for faster rendering (mainly used for silk screen and thermal pads)

# Ground planes and silk screen or larger copper rectangles or circles are filled line-by-line using this resolution.
use constant FILL_WIDTH => .01; #fill at most 0.01 inch at a time

# The max number of characters to read into memory
use constant MAX_BYTES => 10 * M; #bumped up to 10 MB, use const

use constant DUP_DRILL1 => TRUE; #FALSE; #kludge: ViewPlot doesn't load drill files that are too small so duplicate first tool

my $runtime = time(); #Time::HiRes::gettimeofday(); #measure my execution time

print STDERR "Loaded config settings from '${\(__FILE__)}'.\n";
1; #last value must be truthful to indicate successful load


#############################################################################################
#junk/experiment:

#use Package::Constants;
#use Exporter qw(import); #https://perldoc.perl.org/Exporter.html

#my $caller = "pdf2gerb::";

#sub cfg
#{
#    my $proto = shift;
#    my $class = ref($proto) || $proto;
#    my $settings =
#    {
#        $WANT_DEBUG => 990, #10; #level of debug wanted; higher == more, lower == less, 0 == none
#    };
#    bless($settings, $class);
#    return $settings;
#}

#use constant HELLO => "hi there2"; #"main::HELLO" => "hi there";
#use constant GOODBYE => 14; #"main::GOODBYE" => 12;

#print STDERR "read cfg file\n";

#our @EXPORT_OK = Package::Constants->list(__PACKAGE__); #https://www.perlmonks.org/?node_id=1072691; NOTE: "_OK" skips short/common names

#print STDERR scalar(@EXPORT_OK) . " consts exported:\n";
#foreach(@EXPORT_OK) { print STDERR "$_\n"; }
#my $val = main::thing("xyz");
#print STDERR "caller gave me $val\n";
#foreach my $arg (@ARGV) { print STDERR "arg $arg\n"; }

Download Details:

Author: swannman
Source Code: https://github.com/swannman/pdf2gerb

License: GPL-3.0 license

#perl 

Oral  Brekke

Oral Brekke

1655970960

Jest-playwright: Running tests using Jest & Playwright

Jest Playwright

It's more flexible, lightweight, optimized for Playwright, and has TypeScript support out of the box. This doesn't mean, that we stop with maintaining this package.


Running your tests using Jest & Playwright

npm install -D jest jest-playwright-preset playwright

Also you can use jest-playwright-preset with specific playwright packages: playwright-webkit, playwright-chromium and playwright-firefox

npm install -D jest jest-playwright-preset playwright-firefox

Requirements

  • Node.js >= 10.15.0
  • Playwright >= 0.12.1
  • Jest >= 25

Usage

Update your Jest configuration, either:

with package.json:

"jest": {
  "preset": "jest-playwright-preset"
}

or with jest.config.js:

module.exports = {
  preset: 'jest-playwright-preset',
}

And add the Jest command as in the script section of your package.json:

{
  "scripts": {
    "test": "jest"
  }
}

Now you can use Playwright in your tests:

beforeAll(async () => {
  await page.goto('https://whatismybrowser.com/')
})

test('should display correct browser', async () => {
  const browser = await page.$eval('.string-major', (el) => el.innerHTML)
  expect(browser).toContain('Chrome')
})

Notes

playwright actions can take some time for execution, because of it jest-playwright overrides jest default timeout interval from 5 to 15 seconds. You can change this interval with testTimeout in your jest configuration.

Configuration

It's recommend to use a separate Jest configuration jest.e2e.config.js for jest-playwright to gain speed improvements and by that to only use Playwright in the end-to-end tests. For that you have to use the -c flag when calling Jest and use the testMatch or testRegex in your Jest config to split them.

Be sure to remove any existing testEnvironment option from your Jest configuration. The jest-playwright-preset preset needs to manage that option itself.

Configuration options can be specified using a jest-playwright.config.js file at the root of your project:

// jest-playwright.config.js

module.exports = {
  // Options...
}

Similar to Jest globalSetup configuration can except the export of an async function:

module.exports = async () => {
  await ...
};

A custom path can be specified to the jest-playwright.config.js file within your jest.config.js file:

process.env.JEST_PLAYWRIGHT_CONFIG = '/path/to/jest-playwright.config.js'

Alternatively, configuration options can specified using Jest's own testEnvironmentOptions option within your jest.config.js file:

// jest.config.js

module.exports = {
  preset: 'jest-playwright-preset',
  testEnvironmentOptions: {
    'jest-playwright': {
      // Options...
    },
  },
}

Options

  • launchOptions <[object]>. All Playwright launch options can be specified in config. Since it is JavaScript, you can use all stuff you need, including environment.
  • launchType <LAUNCH | PERSISTENT | SERVER>. Method to launch browser instance. jest-playwright attaches Playwright to an existing browser instance by default.
  • connectOptions <[object]>. All Playwright connect options can be specified in config.
  • contextOptions <[object]>. All Playwright context options can be specified in config.
  • browsers <[(string | object)[]]>. Define browsers to run tests in.
    • chromium Each test runs Chromium (default).
    • firefox Each test runs Firefox.
    • webkit Each test runs Webkit.
  • devices <[(string | object)[] | RegExp]>. Define a devices to run tests in. Actual list of devices can be found here.
  • exitOnPageError <[boolean]>. Exits process on any page error. Defaults to true.
  • collectCoverage <[boolean]>. Enables the coverage collection of the saveCoverage(page) calls to the .nyc_output/coverage.json file.
  • serverOptions <[object]>. All jest-process-manager options.
  • selectors <[array]>. Define selectors. Each selector must be an object with name and script properties.
  • skipInitialization <[boolean]>. Add you ability to skip first setup playwright process. Possible use cases can be found here
  • resetContextPerTest <[boolean]>. Option for opening a new context per test
  • useDefaultBrowserType <[boolean]>. Sometimes browser + device combinations don't have any sense. With this option tests will be run with defaultBrowserType of device. Pay attention that you should define devices to correct usage of this option.

Usage of process environment to define browser

You can control the browser with passing environment variable.

// jest-playwright.config.js
module.exports = {
  browsers: [process.env.BROWSER],
}

Specific browser options

For launchOptions, connectOptions and contextOptions you can define special browser options.

// jest-playwright.config.js
module.exports = {
  connectOptions: {
    chromium: {
      wsEndpoint: 'ws://chrome.proxy.com:4444'
    },
    firefox: {
      wsEndpoint: 'ws://firefox.proxy.com:4444'
    }
  },
  ...
}

Browser configuration

There are different ways to define devices in your configuration file:

  • You can use array of browser names:
module.exports = {
  browsers: ["chromium", "webkit"],
  ...
}
  • You can define custom browser. You can find out use cases here:
{
  // Name of browser
  name: 'chromium' | 'firefox' | 'webkit'
  // Display name for test
  displayName: string
  ...
  // Browser options
}

Device configuration

There are different ways to define devices in your configuration file:

  • You can use array of device names:
module.exports = {
  devices: ["iPhone 6", "Pixel 2"],
  ...
}
  • You can use RegExp:
module.exports = {
  devices: /iPhone 8/,
  ...
}
  • Also you can define custom device:
{
  // Name of device
  name: string
  // Page width and height
  viewport: {
    width: number
    height: number
  }
  // user agent
  userAgent: string
  // device scale factor
  deviceScaleFactor: number
  // is device is mobile
  isMobile: boolean
  // support of touch events
  hasTouch: boolean
  // device default browser
  defaultBrowserType: chromium, firefox or webkit
}

Globals

  • browserName <[string]> - name of the current browser (chromium, firefox or webkit)
  • deviceName <[string]> - name of the current device
  • browser <[Browser]> - Playwright browser instance
  • context <[Context]> - a new Playwright context instance for each new test file
  • page <[Page]> - Playwright page instance (since a new context for every test file also creates a new page for it)

All of them are available globally in each Jest test. If you are using ESLint and JavaScript, its recommend to use it in combination with the eslint-plugin-jest-playwright.

Debug mode

Playwright give you ability to configure the browser for debugging with the PWDEBUG environment variable. It will launch the browser in headful mode, disables playwright timeout and Jest won't timeout anymore.:

PWDEBUG=1 jest

Reset helper functions

Reset current page

beforeEach(async () => {
  await jestPlaywright.resetPage()
})

To create a new page for each test, you can use this snippet to have a new page object for each individual test.

Reset current context

beforeEach(async () => {
  await jestPlaywright.resetContext()
})

To create a new context for each test, you can use this snippet to have a new context object for each individual test.

Reset current browser

beforeEach(async () => {
  await jestPlaywright.resetBrowser()
})

You can use this snippet to reset current browser for each individual test. It will reset browser, context and page.

Debug helper functions

jest-playwright provides some functions to debug your tests.

IMPORTANT NOTE: For these kind of tests you should use properties passed through callback function instead of globals

jestPlaywrightDebug

This helper function provide you ability to run specific tests in debug mode. It will disable headless mode. You can find more information here

test.jestPlaywrightDebug('failed', async ({ page }) => {
  await page.goto('https://github.com/')
  const title = await page.title()
  await expect(title).toBe('Google')
})

Also you can define options for debug mode with debugOptions:

// jest-playwright.config.js
module.exports = {
  debugOptions: {
    ...
    contextOptions: {
      offline: true
    }
  }
  ...
}

jestPlaywrightConfig

This helper function provide you ability to run specific tests with passed options. You can define browser and device properties to run test for them, otherwise test run for current configuration.

test.jestPlaywrightConfig(
  {
    // your jest-playwright options
  },
  'test name',
  async ({ browser, context, page }) => {
    /* ... */
  },
)

Tracking the coverage

It's possible to track the coverage of the end-to-end tests with the babel-plugin-istanbul Babel plugin configured. It needs to be included in the web application which you are gonna test otherwise it won't work. To use it, you have to set collectCoverage in the jest-playwright.config.js to true. Per default the test coverage will be automatically saved after each navigation change (beforeunload event). If a certain code path is not covered, you can manually call and add the corresponding saveCoverage(page) call to your tests like that:

await jestPlaywright.saveCoverage(page)

By using coverage collection, it will write the coverage data to the .nyc_output/coverage.json file which can be transformed using nyc to the lcov format:

npx nyc report --reporter=lcovonly

or to HTML:

npx nyc report --reporter=html

which will create a HTML website in the coverage directory.

Skip tests for specific browsers and devices

It's possible to skip tests for browsers or combination of browsers and devices

it.jestPlaywrightSkip(
  { browsers: ['chromium'] },
  'should skip this one',
  async () => {
    const title = await page.title()
    expect(title).toBe('Google')
  },
)

Using shadow DOM selectors

Playwright engine pierces open shadow DOM by default.

beforeAll(async () => {
  await page.goto(
    'https://mdn.github.io/web-components-examples/popup-info-box-web-component/',
  )
})

test('should display "google" text on page', async () => {
  const shadowElem = await page.$('.info')
  const shadowElemText = await shadowElem.innerHTML()

  expect(shadowElemText).toBe(
    'Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card.',
  )
})

Start a server

Jest Playwright integrates a functionality to start a server when running your test suite, like jest-puppeteer. It automatically closes the server when tests are done.

To use it, specify a server section in your jest-playwright.config.js.

// jest-playwright.config.js
module.exports = {
  serverOptions: {
    command: 'node server.js',
    port: 4444,
  },
}

Other options are documented in jest-process-manager.

Using with different jest environments

The default jest-playwright environment is node, but you can use a browser-like environment through jest-playwright-jsdom

expect-playwright

There is a utility package expect-playwright which simplifies the expect statements in combination with Playwright to make e.g. shorter text comparisons.

ESLint globals / 'page' is not defined

There is an ESLint plugin available eslint-plugin-jest-playwright available which includes the globals for using jest-playwright.

Unstable and experimental API

You can run tests for multiple browsers and devices:

  • You must have installed the playwright package
  • You must define browsers to test with your jest-playwright.config.js:
module.exports = {
    browsers: ["chromium", "webkit"],
    devices: ["iPhone 6", "Pixel 2"],
    ...
}

It will run your tests for:

  • Chromium browser and iPhone 6 device;
  • Chromium browser and Pixel 2 device;
  • Webkit browser and iPhone 6 device;
  • Webkit browser and Pixel 2 device;

If there is no defined browsers in config it will run tests for chromium browser.

Usage with custom testEnvironment

You can use jest-playwright with custom test environment for taking screenshots during test failures for example:

jest.config.json

"testEnvironment": "./CustomEnvironment.js"

CustomEnvironment.js

const PlaywrightEnvironment = require('jest-playwright-preset/lib/PlaywrightEnvironment')
  .default

class CustomEnvironment extends PlaywrightEnvironment {
  async setup() {
    await super.setup()
    // Your setup
  }

  async teardown() {
    // Your teardown
    await super.teardown()
  }

  async handleTestEvent(event) {
    await super.handleTestEvent(event);
    if (event.name === 'test_done' && event.test.errors.length > 0) {
      const parentName = event.test.parent.name.replace(/\W/g, '-')
      const specName = event.test.name.replace(/\W/g, '-')

      await this.global.page.screenshot({
        path: `screenshots/${parentName}_${specName}.png`,
      })
    }
  }
}

module.exports = CustomEnvironment

Usage with custom runner

jest-playwright using custom runner underhood. So if you need implement your own runner, you should extend it:

jest.config.json

"runner": "./CustomRunner.js"

CustomRunner.js

const PlaywrightRunner = require('jest-playwright-preset/lib/PlaywrightRunner')
  .default

class CustomRunner extends PlaywrightRunner {
  constructor(...args) {
    super(...args)
    this.isSerial = true
  }
}

module.exports = CustomRunner

Usage with custom globalSetup and globalTeardown

For this use case, jest-playwright-preset exposes two methods: globalSetup and globalTeardown, so that you can wrap them with your own global setup and global teardown methods as the following example:

Getting authentication state once for all test cases as per playwright reference:

// global-setup.js
import { globalSetup as playwrightGlobalSetup } from 'jest-playwright-preset';

module.exports = async function globalSetup(globalConfig) {
  await playwrightGlobalSetup(globalConfig);

  const browserServer = await chromium.launchServer();
  const wsEndpoint = browserServer.wsEndpoint();
  const browser = await chromium.connect({ wsEndpoint: wsEndpoint });
  const page = await browser.newPage();

  // your login function
  await doLogin(page);

  // store authentication data
  const storage = await page.context().storageState();
  process.env.STORAGE = JSON.stringify(storage);
};
// global-teardown.js
import { globalTeardown as playwrightGlobalTeardown } from 'jest-playwright-preset';

module.exports = async function globalTeardown(globalConfig) {
  // Your global teardown
  await playwrightGlobalTeardown(globalConfig);
}

Then assigning your js file paths to the globalSetup and globalTeardown property in your Jest configuration.

{
  // ...
  "globalSetup": "./global-setup.js",
  "globalTeardown": "./global-teardown.js"
}

Now your custom globalSetup and globalTeardown will be triggered once before and after all test suites.

Usage with Typescript

Example Jest configuration in combination with ts-jest:

module.exports = {
  preset: 'jest-playwright-preset',
  transform: {
    '^.+\\.ts$': 'ts-jest',
  },
}

Types are also available, which you can either use via directly in your test:

/// <reference types="jest-playwright-preset" />
/// <reference types="expect-playwright" />

or at your central tsconfig.json either via files:

{
  "files": [
    "./global.d.ts",
    "node_modules/jest-playwright-preset/types/global.d.ts",
    "node_modules/expect-playwright/global.d.ts"
  ]
}

or via types:

{
  "compilerOptions": {
    "types": ["jest-playwright-preset", "expect-playwright"]
  }
}

It's important to not change the testEnvironment to node. Otherwise it won't work.

Known issues

Error reporting with Jest

If you face into error messages like

UnhandledPromiseRejectionWarning: Error: Protocol error (Runtime.callFunctionOn): Target closed.

or

Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Error:

and your Jest error reporting will only show that an entire test (it() function) has failed, then you need to increase the Jest timeout because the Playwright timeout is greater than the Jest timeout. So Jest in the end will simply stop the execution and no verbose (which exact line) error reporting can be generated.

To fix this behavior simply call

jest.setTimeout(35 * 1000)

in your tests at the top. (30 seconds is the default Playwright timeout for waiting for an specific element.)

New Browser instance for each test

If for your individual tests a new entire browser instance spins up each time and it won't be reused, then you probably run them in parallel. If you run them in a synchronous way with the --runInBand CLI option for Jest, then the same browser instance will be re-used and this should fix the issue.

Examples

Demonstration the usage of jest-playwright for various test cases can be found in playwright-jest-examples

Inspiration

Thanks to Smooth Code for the great jest-puppeteer.

⚠️ We recommend the official Playwright test-runner (@playwright/test) ⚠️

Author: Playwright-community
Source Code: https://github.com/playwright-community/jest-playwright/ 
License: MIT license

#node #jest #playwright #testing #typescript 

What is Playwright Test | How to use Playwright Test to Run Unit Tests

A comparison of Playwright's new test runner versus 'traditional' approaches like Jest and Mocha

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API. Playwright is built to enable cross-browser web automation that is ever-green, capable, reliable and fast.

Why you might want to use Playwright Test as a test runner for unit tests, and how to fill in missing pieces (e.g. code coverage).

In this blog post, I want to

  • describe what I like about Playwright and why I think it can be a good choice for unit testing
  • outline things missing from Playwright compared to other test frameworks (and how to fill some of those gaps)
  • suggest some minor tweaks to make Playwright more suitable for unit tests

Why to use Playwright Test for unit tests

I had used "traditional" test runners before, mainly Jest, but also Mocha a few years ago.
I think there are some advantages of using Playwright for unit tests:

Same tool for E2E tests and unit tests: One tool less in your setup means one tool less to learn, configure, update, etc.

Out-of-the-box support for TypeScript: Playwright Test supports TypeScript out-of-the-box.
For all other test runners I am aware of you have to configure some transform (Babel, ts-jest, etc.), or run the test runner with ts-node, or compile the test cases before running them (see e.g. "Using Typescript" of the Jest docs).

Access to features unique to Playwright: Playwright has some very useful features, e.g.:

"fixtures" allow to prepare resources per test case on an "opt-in" basis.
This is in my opinion much better than the beforeEach / afterEach fiddling you have to do with other test runners.

// base.ts
import { test as base } from '@playwright/test';
import * as sinon from 'sinon';

export const test = base.extend<{ fakeClock: sinon.SinonFakeTimers }>({
  fakeClock: [
    async ({}, use) => {
      const clock = sinon.useFakeTimers();
      await use(clock);
      clock.restore();
    },
    { scope: 'test' },
  ],
});

// FILE: bar.spec.ts
import { test } from './base';

test('with real clock', ({}) => {
  /*
   * this test case does not use the "fakeClock" fixture,
   * thus the fixture does not get set up
   */
});
test('with fake clock', ({ fakeClock }) => {
  /*
   * fake timers will get set up for this test case
   * because "fakeClock" fixture is used
   */
});

"projects" allow to run "the same or different tests in multiple configurations".
For example a while ago I had to make sure that some client/server interaction based on socket.io works with both WebSockets and HTTP long-polling.
Setting up two Playwright projects with different project parameters allowed me to run the same tests with two configurations, once using WebSockets and once using HTTP long-polling:

// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';

interface TestOptions {
  transport: 'websocket' | 'polling';
}

const config: PlaywrightTestConfig<TestOptions> = {
  projects: [
    {
      name: 'using transport: websocket',
      use: { transport: 'websocket' },
    },
    {
      name: 'using transport: polling',
      use: { transport: 'polling' },
    },
  ],
};

export default config;

// FILE: bar.spec.ts
import { test } from '@playwright/test';

test('socket.io connection', ({ transport }) => {
  /*
   * this test case will get run twice,
   * with "transport" set once to "websocket" and once to "polling"
   */
  initializeSocket(transport);
  // ...
});

Development funded by a big company: Playwright is a product of Microsoft and has been maintained very actively over the last couple of years.

No use of globals: Playwright does not use any globals for its test runner. You import from @playwright/test just what you need in your test files:

// FILE bar.spec.ts
import { expect, test } from '@playwright/test';

test.describe('describe title', () => {
  test('test title', ({}) => {
    expect(1 + 1).toEqual(2);
  });
});

Compare that to existing test runners like Jest and Mocha (and Cypress, using Mocha under-the-hood). Those test runners set up their test functions as globals (it, expect, describe, beforeEach etc.).
In TypeScript projects, as soon as you install any two of these test libraries you end up with TypeScript compilation errors, since the types of those functions are usually not compatible with each other. There are a couple of workarounds to avoid those compilation errors, for example fiddling with (multiple) tsconfig.json files such that types of these libraries are excluded.
Cypress even has a guide dedicated to this problem (docs.cypress.io/guides/tooling/typescript-support#Clashing-types-with-Jest).

Having the same import statements from @playwright/test in every test file might be some boilerplate, but I think the reduced friction (by not having any globals) justifies that boilerplate.
Newer test runners like vitest also do not set globals anymore.

Things missing from Playwright (and how to fill the gaps)

Compared to Jest, there are some things missing from Playwright Test which are often needed when running unit tests.
Most of those things can be substituted by using libraries of the Node.js ecosystem.
Take a look at the example repository I made to see how to set up those things!

For Mocks & Spies use sinon or jest-mock (the mocking solution shipped with Jest).

For Fake timers use sinon (See code changes).

Code coverage just needs a simple combination of the command-line interface nyc of Istanbul (a code coverage tool for JavaScript) and source-map-support (See code changes).

Watch mode is not supported but "planned work" at the time of writing: playwright.dev/docs/test-components#planned-work.
There are some workarounds in the related issue: github.com/microsoft/playwright/issues/7035.

Some test frameworks provide a way to mock CommonJS/ECMAScript modules.
Module mocking allows to replace the "thing" returned by require/import statements by some fake instance.

Unfortunately, this is not possible in Playwright. Although there are some Node.js libraries that allow module mocking (like testdouble), they don't work because Playwright does some of the module loading itself (github.com/microsoft/playwright/issues/14398).
If you want to get module mocking support in Playwright, you can upvote this feature request: github.com/microsoft/playwright/issues/14572.

This is the biggest downside of using Playwright for unit tests in my opinion.
For now, if you want to test modules isolated from others you probably have to adapt the codebase to use some kind of dependency injection. Be it that dependencies are just passed as arguments when calling functions, or using a dependency injection solution like NestJS IoC containers (which I have used very successfully in the past).

One last thing

Playwright Test has a method expect(...).toMatchSnapshot(...) which can perform snapshot-comparisons on screenshots but also textual and binary data.

At the time of writing, Playwright will append a platform-specific suffix (like linux) to the name of the file the snapshot is stored in. This makes sense for screenshots (since screenshots taken on different platforms will deviate slightly, for example because of text rendering), but it rarely makes sense in case of textual/binary data.

This is an open issue (github.com/microsoft/playwright/issues/11134). An easy workaround is to disable the suffix by setting up an "auto-fixture" and override testInfo.snapshotSuffix:

import { test as base } from '@playwright/test';

export const test = base.extend<{ _autoSnapshotSuffix: void }>({
  _autoSnapshotSuffix: [
    async ({}, use, testInfo) => {
      testInfo.snapshotSuffix = '';
      await use();
    },
    { auto: true },
  ],
});

#testing #playwright #test 

Tamia  Walter

Tamia Walter

1596754901

Testing Microservices Applications

The shift towards microservices and modular applications makes testing more important and more challenging at the same time. You have to make sure that the microservices running in containers perform well and as intended, but you can no longer rely on conventional testing strategies to get the job done.

This is where new testing approaches are needed. Testing your microservices applications require the right approach, a suitable set of tools, and immense attention to details. This article will guide you through the process of testing your microservices and talk about the challenges you will have to overcome along the way. Let’s get started, shall we?

A Brave New World

Traditionally, testing a monolith application meant configuring a test environment and setting up all of the application components in a way that matched the production environment. It took time to set up the testing environment, and there were a lot of complexities around the process.

Testing also requires the application to run in full. It is not possible to test monolith apps on a per-component basis, mainly because there is usually a base code that ties everything together, and the app is designed to run as a complete app to work properly.

Microservices running in containers offer one particular advantage: universal compatibility. You don’t have to match the testing environment with the deployment architecture exactly, and you can get away with testing individual components rather than the full app in some situations.

Of course, you will have to embrace the new cloud-native approach across the pipeline. Rather than creating critical dependencies between microservices, you need to treat each one as a semi-independent module.

The only monolith or centralized portion of the application is the database, but this too is an easy challenge to overcome. As long as you have a persistent database running on your test environment, you can perform tests at any time.

Keep in mind that there are additional things to focus on when testing microservices.

  • Microservices rely on network communications to talk to each other, so network reliability and requirements must be part of the testing.
  • Automation and infrastructure elements are now added as codes, and you have to make sure that they also run properly when microservices are pushed through the pipeline
  • While containerization is universal, you still have to pay attention to specific dependencies and create a testing strategy that allows for those dependencies to be included

Test containers are the method of choice for many developers. Unlike monolith apps, which lets you use stubs and mocks for testing, microservices need to be tested in test containers. Many CI/CD pipelines actually integrate production microservices as part of the testing process.

Contract Testing as an Approach

As mentioned before, there are many ways to test microservices effectively, but the one approach that developers now use reliably is contract testing. Loosely coupled microservices can be tested in an effective and efficient way using contract testing, mainly because this testing approach focuses on contracts; in other words, it focuses on how components or microservices communicate with each other.

Syntax and semantics construct how components communicate with each other. By defining syntax and semantics in a standardized way and testing microservices based on their ability to generate the right message formats and meet behavioral expectations, you can rest assured knowing that the microservices will behave as intended when deployed.

Ways to Test Microservices

It is easy to fall into the trap of making testing microservices complicated, but there are ways to avoid this problem. Testing microservices doesn’t have to be complicated at all when you have the right strategy in place.

There are several ways to test microservices too, including:

  • Unit testing: Which allows developers to test microservices in a granular way. It doesn’t limit testing to individual microservices, but rather allows developers to take a more granular approach such as testing individual features or runtimes.
  • Integration testing: Which handles the testing of microservices in an interactive way. Microservices still need to work with each other when they are deployed, and integration testing is a key process in making sure that they do.
  • End-to-end testing: Which⁠—as the name suggests⁠—tests microservices as a complete app. This type of testing enables the testing of features, UI, communications, and other components that construct the app.

What’s important to note is the fact that these testing approaches allow for asynchronous testing. After all, asynchronous development is what makes developing microservices very appealing in the first place. By allowing for asynchronous testing, you can also make sure that components or microservices can be updated independently to one another.

#blog #microservices #testing #caylent #contract testing #end-to-end testing #hoverfly #integration testing #microservices #microservices architecture #pact #testing #unit testing #vagrant #vcr

Software Testing 101: Regression Tests, Unit Tests, Integration Tests

Automation and segregation can help you build better software
If you write automated tests and deliver them to the customer, he can make sure the software is working properly. And, at the end of the day, he paid for it.

Ok. We can segregate or separate the tests according to some criteria. For example, “white box” tests are used to measure the internal quality of the software, in addition to the expected results. They are very useful to know the percentage of lines of code executed, the cyclomatic complexity and several other software metrics. Unit tests are white box tests.

#testing #software testing #regression tests #unit tests #integration tests