microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
66.43k stars 3.63k forks source link

[Question] Test hangs after all steps have completed successfully #11057

Closed ElliottDevlinPA closed 2 years ago

ElliottDevlinPA commented 2 years ago

Hi,

I am using Playwright Test v1.17.1 with TypeScript and have implemented the Page Object Model into my project.

I am experiencing an issue with one of my tests whereby all of the steps complete successfully, but the browser remains open for anywhere between 10 and 30 seconds rather than closing and finishing the test run. It seems to hang longer when run headless. This issue doesn't occur with my other tests - the only difference I can see between them is that the test with the issue interacts with two page objects.

This is my spec file...

import { test, expect } from '@playwright/test';
import { Story } from '../interfaces/story';
import { LoginPage } from '../pages/login-page';
import { StoryPage } from '../pages/story-page';

test.describe.parallel('Story Publish Tests', () => {

    test.beforeEach(async ({ page }) => {
      const loginPage = new LoginPage(page);
      await loginPage.goto();
    });

    test('Publish Text-Only Story', async ({ page }) => {
      const loginPage = new LoginPage(page);
      const storyPage = new StoryPage(page);

      const storyData: Story = {
          title: 'Text-Only Story Title',
          abstract: 'Text-Only Story Abstract',
          body: 'Text-Only Story Body Text',
          replacementByline: 'Test Bot',
          tags: 'Playwright',
          postType: 'story',
          format: { textOnly: true },
          topics: {topic: 'Adventure',  keyword: 'Auto-Tests', services: {service: ['News'], subService: ['News: UK']}, territory: ['UK']}
      }

      await loginPage.loginAs('editor');
      await storyPage.addStoryData(storyData);
      await storyPage.clickPublishButton();
      await storyPage.assertOnPendingTab();
    });

});

This is my Page Object...

import { expect, Locator, Page } from '@playwright/test';
import { Story } from '../interfaces/story';
const short = require('short-uuid');

export class StoryPage {
    readonly page: Page;
    readonly titleField: Locator;
    readonly abstractField: Locator;
    readonly bodyField: Locator;
    readonly replacementBylineField: Locator;
    readonly tagsField: Locator;
    readonly notesAndCorrectionsBox: Locator;
    readonly enrichedOnlyCheckbox: Locator;
    readonly featuredCheckbox: Locator;
    readonly nonMediaCheckbox: Locator;
    readonly compositeRadioButton: Locator;
    readonly textOnlyRadioButton: Locator;
    readonly postTypeDropdown: Locator;
    readonly topicsDropdown: Locator;
    readonly topicsDropdownSearchField: Locator;
    readonly topicsSearchResult: Locator;
    readonly topicsList: Locator;
    readonly keywordsField: Locator;
    readonly servicesDropdown: Locator;
    readonly servicesList: Locator;
    readonly territoriesDropdown: Locator;
    readonly territoriesList: Locator;
    // ToDo: Move to Dashboard page
    readonly addNewButton: Locator;
    readonly storiesDashboardLink: Locator;
    readonly publishButton: Locator;
    readonly previewButton: Locator;

    constructor(page: Page) {
        this.page = page;
        this.titleField = page.locator('#title');
        this.abstractField = page.locator('#excerpt');
        this.bodyField = page.locator('#content_ifr');
        this.replacementBylineField = page.locator('#byline');
        this.tagsField = page.locator('#pa-nlp-tagger-manual-tag');
        this.notesAndCorrectionsBox = page.locator('textarea[name=\'pa_post_note\']');
        this.enrichedOnlyCheckbox = page.locator('//label[text()=\' Enriched-only\']/input');
        this.featuredCheckbox = page.locator('//label[text()=\' Featured\']/input')
        this.nonMediaCheckbox = page.locator('//label[text()=\' Non-media\']/input')
        this.compositeRadioButton = page.locator('#post-format-6composite');
        this.textOnlyRadioButton = page.locator('#post-format-2text');
        this.postTypeDropdown = page.locator('select[name=\'pa_story_post_type\']');
        this.topicsDropdown = page.locator('#select2-pa-topic-select-container');
        this.topicsDropdownSearchField = page.locator('span.select2-search.select2-search--dropdown > input');
        this.topicsSearchResult = page.locator('.select2-container .select2-results__option--highlighted');
        this.topicsList = page.locator('#pa-topic-list li');
        this.keywordsField = page.locator('#new-tag-pa_keyword');
        this.servicesDropdown = page.locator('#select2-pa-service-select-container');
        this.servicesList = page.locator('#pa-service-list li');
        this.territoriesDropdown = page.locator('#select2-pa-territory-select-container');
        this.territoriesList = page.locator('#pa-territory-list li');
        this.addNewButton  = page.locator('.page-title-action');
        this.storiesDashboardLink = page.locator('a[class=\'wp-not-current-submenu menu-top toplevel_page_edit?post_type=story&post_status=pending\']');
        this.publishButton = page.locator('#publish');
        this.previewButton = page.locator('#post-preview');
    }

    async addStoryData(storyData: Story) {
        const uuid = short.generate();

        await this.storiesDashboardLink.click();
        await this.addNewButton.click();

        await this.titleField.fill(uuid + ' ' + storyData.title);
        await this.abstractField.fill(storyData.abstract);
        await this.enterBodyText(storyData.body)
        await this.replacementBylineField.fill(storyData.replacementByline);
        await this.tagsField.fill(storyData.tags);
        await this.page.keyboard.press('Enter');
        await this.selectTopic(storyData.topics.topic);
        await this.addKeyword(storyData.topics.keyword);
        await this.textOnlyRadioButton.click();
        await this.getPostId();
    }

    async enterBodyText(body: string) {
      const elementHandle = await this.page.$('#content_ifr');
      const frame = await elementHandle?.contentFrame();
      await frame.fill('#tinymce', body);
    }

    async selectTopic(topic: string) {
      await this.topicsDropdown.click();
      await this.topicsDropdownSearchField.fill(topic);
      await this.topicsSearchResult.click();
    }

    async addKeyword(topic: string) {
      await this.keywordsField.fill(topic);
      await this.page.keyboard.press('Enter');
      await this.page.click('.main');
    }

    async getPostId() {
      const href = await this.previewButton.getAttribute('href');
      const postId = href.split('p=')[1].split('&')[0].trim();
      console.log('PostID = ' + postId);
    }

    async clickPublishButton() {
      this.page.on('dialog', dialog => dialog.accept());
      await this.publishButton.click();
    }

    async assertOnPendingTab() {
      expect(this.page.url()).toContain('post_status=pending');
    }

}

And this is my config file...

import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
  reporter: [ ['html', { open: 'never' }] ],
  timeout: 120000,
  reportSlowTests: null,
  use: {
    headless: false,
    browserName: 'chromium',
    ignoreHTTPSErrors: true,
    video: 'on',
    screenshot: 'only-on-failure',
    trace: 'retain-on-failure',
    actionTimeout: 30000,
    launchOptions: {
      slowMo: 100,
    }
  },
};
export default config;

Are you able to offer any help please?

yury-s commented 2 years ago

Can you record and upload trace for the failing test case? Also you are reusing the same page for to page object models concurrently, is that expected or they are supposed to be two different browser pages (tabs) ?

      const loginPage = new LoginPage(page);
      const storyPage = new StoryPage(page);
ElliottDevlinPA commented 2 years ago

Hi @yury-s, thanks for your response.

Just to clarify - the test isn't failing in that all of the steps complete successfully, the issue is that the session hangs after all of the steps have been completed instead of closing down immediately as would be expected. Re: the two pages, that is expected in that the test only requires a single window/tab.

Regarding the Trace, I wasn't sure which file you need so have uploaded a couple of things - please let me know if they are incorrect and how to find the right file.

Many thanks, Elliott.

trace.zip 0d8b98138b7967e0a5b598ef807eda1dc03f8e95.zip

aslushnikov commented 2 years ago

@ElliottDevlinPA can you try running with video: 'off' in your config? Also, what's your OS?

ElliottDevlinPA commented 2 years ago

Hi @aslushnikov. I tried setting video to off but still experience the issue. I am on MacOS Big Sur v11.6.1.

ElliottDevlinPA commented 2 years ago

@aslushnikov I think I've just found the source of the issue - when I set Trace to 'of' in the config, the issue no longer occurs. However, if the value is 'on' or 'retain-on-failure', the issue occurs.

aslushnikov commented 2 years ago

@ElliottDevlinPA can you please create a simple test repository with all your files so that we can run it locally and debug the behavior?

ElliottDevlinPA commented 2 years ago

@aslushnikov unfortunately I don't think I will be able to do that as the application I am testing is not publicly accessible. I will try to replicate the issue against a different site and will create a test repo if I am successful. At least I have a workaround in the meantime. Thanks.

aslushnikov commented 2 years ago

@ElliottDevlinPA thanks! Please file another issue if / when you have a repro!

jamalebp-formant commented 2 years ago

@aslushnikov was the issue ever tested against a public repo??

forelock7 commented 1 year ago

The same issue I reproduced in 1.36.0