After a long hiatus due to moving and a busy family life, I thought I’d resurrect this substack. I will start with a little way to improve developer experience with Playwright that I recently came up with.
In writing automated tests, it is often necessary to log into your application using test users of various configurations. A common way of handling this is through environment variables. Playwright handles this using the dotenv package. The Playwright docs even give a nice example of how to do it here.
In release 1.31.0 of playwright, they introduced the concept of dependencies. This allows you to easily set a setup test to log in and capture user session data for each test user before running your main suite, and then gain a test trace if that setup fails.
One pain point that I’ve seen with .env
files is that they rely on developers to create and maintain something like an example.env
file in their repo. Unlike the true .env
file, they are checked into source control with the intention of providing a list of required variables for your application to run. Unfortunately, there is nothing other than code reviews to make sure these examples stay in sync. If a developer is missing a value in this, it takes a little work to understand why tests fails. It would be great if you could do this programmatically.
Enter Zod for making the tests easier to debug. Zod is a “TypeScript-first schema declaration and validation library” that is gaining popularity in the Typescript world. We can leverage it in an initial Playwright test before running our setup login tests.
First, we can create a env.ts file in the root of our project with the desired variables:
import { z } from 'zod';
export const envVariableSchema = z.object({
ALLOW_SOMETHING_BOOLEAN: z.enum(['true', 'false']),
USER_EMAIL: z.string().email(),
USER_PW: z.string(),
ADMIN_EMAIL: z.string().email(),
ADMIN_PW: z.string(),
});
We can then use these variables as needed in the setup file:
import { test as setup, expect } from '@playwright/test';
import { envVariableSchema } from './env';
let ENV = {};
// safeParse here to constrain failure to check in validateENV test
const result = envVariableSchema.safeParse(process.env);
if (result.success){
ENV = envVariableSchema.parse(process.env)
}
const testUsers = {
user: {
email: ENV.USER_EMAIL || '',
password: ENV.USER_PW || '',
},
admin: {
email: ENV.ADMIN_EMAIL || '',
password: ENV.ADMIN_PW || '',
},
}
setup.describe('login and save Storage state of console users', () => {
for (const [user, credentials] of Object.entries(testUsers)) {
setup(
`login as ${user}`,
async () => {
.... Your login steps
}
);
}
});
We also create a new test to do our validation which we’ll call validateEnv.ts
:
import { test as setup, expect } from '@playwright/test';
import { envVariableSchema } from './env';
const checkUnicode = `\u2713`;
setup.describe('Validate Env Variables are in place', () => {
setup('validate against schema', () => {
const variables = process.env;
const result = envVariableSchema.safeParse(variables);
if (!result.success) {
// log error to show in trace then return
console.error(result.error);
} else {
console.info(
`${checkUnicode} env variables are valid`
);
}
expect(result.success, 'The environment variables are valid').toBeTruthy();
});
});
Finally, we update our Playwright Config to run the Environment Variable Validation, then run our Setup to grab user sessions, and then finally run your test suite if both of those pass:
...
projects: [
{
name: 'validateEnv',
testMatch: '**/validateEnv.ts',
},
{
name: 'setup',
testMatch: '**/login.setup.ts',
dependencies: ['validateEnv'],
},
{
name: 'chromium',
use: devices['Desktop Chrome'],
dependencies: ['setup'],
},
],
....
Now, when you run tests, you will get a clear error message if the variable is missing or if it has an incorrect format!
Stay tuned for more articles, and if you try this and like it, then let me know.