How do you structure a Jest/Puppeteer test?

Hello lovely people!

In the last couple of years, we have been using Jest with Puppeteer to manage our end-to-end tests. Over this time, we have had some serious learnings about the right and wrong way to approach this sort of thing!

We are now at a key point where we want to roll this approach out to other teams across the business, and as part of this some questions are naturally emerging. Namely about how we use the Describe/It blocks to structure our tests in the spec files. There are two main suggestions at the moment. Like this:

describe('Full flow for adding sites, assets and sensors', () => {

    beforeAll(async () => {

    });
    afterAll(async () => {

    });
    describe('When a super admin user navigates to sites page via admin page', () => {
        beforeAll(async () => {

        });
        it('should display sites page with correct URL and contain add button, search box, and sites table', async () => {

        });
        describe('When a super admin user is on the sites page and adds a site', () => {
            beforeAll(async () => {

            });
            it('should display new page with correct URL', async () => {

            });
            it('should be able to add a new site', async () => {

            });
            it('should navigate user to edit site page which contains an asset management pane', async () => {

            });
        });
        describe('When a super admin user is on the sites edit page and adds an asset', () => {
            beforeAll(async () => {

            });
            it('should display a new page with correct URL', async () => {

            });
            it('should be able to add a new asset', async () => {

            });
            it('should navigate user to edit asset page which contains a sensor management pane', async () => {

            });
        });
        describe('When a super admin user is on the assets edit page and adds a sensor', () => {
            beforeAll(async () => {

            });
            it('should display a new page with correct URL', async () => {

            });
            it('should be able to add a new sensor', async () => {

            });
            it('should navigate user to edit asset page', async () => {

            });
        });
    });
});

…or like this:

describe('Admin user flows', () => {

    beforeAll(async () => {

    });
    afterAll(async () => {

    });
    describe('Adding sites, assets and sensors', () => {
        beforeAll(async () => {

        });
		it('navigate to sites page', async () => {

		});
		it('add a site', async () => {
		
		});
		it('navigate to edit site page which contains an asset management pane', async () => {

		});
		it('add a new asset', async () => {

		});
		it('navigate to edit asset page which contains a sensor management pane', async () => {

		});
		it('add a new sensor', async () => {

		});
    });
});

The main difference (as I see it) being that the first example uses more describe blocks to have a cadence of “cause, effect, cause, effect”, whereas the second example goes “step, step, step, step” with the cause and effect both going into an It block.

How traditional/non-traditional are either approach? Can you see any obvious pitfalls? How do you guys structure your tests?

2 Likes