Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playwright: use an interface for passing TestFile paths. #55693

Merged
merged 3 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/calypso-e2e/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as BrowserManager from './browser-manager';
import * as DataHelper from './data-helper';
import * as ElementHelper from './element-helper';
import * as MediaHelper from './media-helper';
export type { TestFile } from './media-helper';

export { BrowserHelper, BrowserManager, MediaHelper, DataHelper, ElementHelper };

Expand Down
103 changes: 55 additions & 48 deletions packages/calypso-e2e/src/media-helper.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { constants } from 'fs';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import config from 'config';
import { getTimestamp } from './data-helper';

/**
* Interface for holding various parts of a filepath.
*/
export interface TestFile {
fullpath: string; // eg. /usr/home/wp-calypso/test/e2e/image-uploads/image.jpg
dirname: string; // eg. /usr/home/wp-calypso/test/e2e/image-uploads/
basename: string; // eg. image.jpg
filename: string; // eg. image
extension: string; // eg. .jpg
}

const artifacts: { [ key: string ]: string } = config.get( 'artifacts' );

/**
Expand Down Expand Up @@ -45,61 +57,56 @@ export function getVideoDir(): string {
/**
* Creates a temporary test file by cloning a source file under a new name.
*
* @param {string} sourcePath Full path on disk of the source file.
* @param {{[key: string]: string}} param0 Parameter object.
* @param {string} param0.sourceFileName Basename of the source file to be cloned.
* @param {string} [param0.testFileName] Basename of the test file to be generated.
* @returns {Promise<string>} Full path to the generated test file.
* @param {string} [param0.postfix] Additional suffix to be used for the file.
* @returns {Promise<TestFile>} Object implementing the TestFile interface.
* @throws {Error} If source file was not found, or source file did not contain an extension.
*/
export async function createTestFile( {
sourceFileName,
testFileName,
}: {
sourceFileName: string;
testFileName?: string;
} ): Promise< string > {
let fileName = getTimestamp();
// If the output `testFileName` is defined, use that as part of the final filename.
if ( testFileName ) {
fileName += `-${ testFileName }`;
export async function createTestFile(
sourcePath: string,
{
postfix,
}: {
postfix?: string;
} = {}
): Promise< TestFile > {
// Check whether the source file maps to a file.
// Note, if sourcePath is not found use console.error instead of throw:
// https://github.com/facebook/jest/issues/8688
try {
await fs.access( sourcePath );
} catch {
throw new Error( `Source file ${ sourcePath } not found on disk.` );
}

// Reassign the variable with the final name to be used, including the extension.
fileName = `${ fileName }.${ sourceFileName.split( '.' ).pop() }`;

const sourceFileDir = path.join( __dirname, '../../../../../test/e2e/image-uploads/' );
const sourceFilePath = path.join( sourceFileDir, sourceFileName );
// Obtain the file extension.
const extension = path.extname( sourcePath );
if ( ! extension ) {
throw new Error( `Extension not found on source file ${ sourcePath }` );
}

const tempDir = await fs.mkdtemp( path.join( os.tmpdir(), 'e2e-' ) );
const testFilePath = path.join( tempDir, fileName );
// Generate a filename using current timestamp and a pseudo-randomly generated integer.
let filename = getTimestamp();
// If `postfix` is defined, use that as part of the final filename.
if ( postfix ) {
filename += `-${ postfix }`;
}

await fs.copyFile( sourceFilePath, testFilePath );
// Obtain the basename (filename with extension)
const basename = `${ filename }${ extension }`;

return testFilePath;
}

/**
* Returns the path to a generated temporary JPEG image file.
*
* @returns {Promise<string>} Full path on disk to the generated test file.
*/
export async function createTestImage(): Promise< string > {
return await createTestFile( { sourceFileName: 'image0.jpg' } );
}
const tempDir = await fs.mkdtemp( path.join( os.tmpdir(), 'e2e' ) );
const targetPath = path.join( tempDir, basename );

/**
* Returns the path to a generated temporary MP3 audio file.
*
* @returns {string} Full path on disk to the generated test file.
*/
export async function createTestAudio(): Promise< string > {
return await createTestFile( { sourceFileName: 'bees.mp3' } );
}
await fs.copyFile( sourcePath, targetPath, constants.COPYFILE_EXCL );

/**
* Returns the path to an unsupported file.
*
* @returns {string} Full path on disk to the generated test file.
*/
export async function createInvalidFile(): Promise< string > {
return await createTestFile( { sourceFileName: 'unsupported_extension.mkv' } );
// Return an object implementing the interface.
return {
fullpath: targetPath,
dirname: tempDir,
basename: basename,
filename: filename,
extension: extension,
};
}
12 changes: 12 additions & 0 deletions test/e2e/lib/jest/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const JestEnvironmentNode = require( 'jest-environment-node' );

class JestEnvironmentE2E extends JestEnvironmentNode {
testFailed = false;
hookFailed = false;

async handleTestEvent( event ) {
switch ( event.name ) {
Expand All @@ -16,9 +17,20 @@ class JestEnvironmentE2E extends JestEnvironmentNode {
if ( this.testFailed ) {
event.test.mode = 'skip';
}
// With this flag enabled, all test cases in the describe
// block will be marked as failed.
// This way all other hooks (screenshot/recording) are run,
// and Jest correctly exits after those hooks are run.
if ( this.hookFailed ) {
event.test.mode = 'fail';
}
break;

case 'hook_failure':
this.global.__CURRENT_TEST_FAILED__ = true;
this.hookFailed = true;
break;

case 'test_fn_failure':
this.global.__CURRENT_TEST_FAILED__ = true;
this.testFailed = true;
Expand Down
11 changes: 11 additions & 0 deletions test/e2e/specs/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import path from 'path';

export const TEST_IMAGE_PATH = path.normalize(
path.join( __dirname, '..', 'image-uploads', 'image0.jpg' )
);
export const TEST_AUDIO_PATH = path.normalize(
path.join( __dirname, '..', 'image-uploads', 'bees.mp3' )
);
export const UNSUPPORTED_FILE_PATH = path.normalize(
path.join( __dirname, '..', 'image-uploads', 'unsupported_extension.mkv' )
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file defines the paths where the test files are located.

Test writers should import the appropriate const and pass it into the createTestFile method.

15 changes: 8 additions & 7 deletions test/e2e/specs/specs-playwright/wp-blocks__coblocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* @group gutenberg
*/

import path from 'path';
import {
setupHooks,
DataHelper,
Expand All @@ -15,27 +14,29 @@ import {
HeroBlock,
ClicktoTweetBlock,
LogosBlock,
TestFile,
} from '@automattic/calypso-e2e';
import { Page } from 'playwright';
import { TEST_IMAGE_PATH } from '../constants';

describe( DataHelper.createSuiteTitle( 'Blocks: CoBlocks' ), () => {
describe( DataHelper.createSuiteTitle( 'Blocks: CoBlocks' ), function () {
let gutenbergEditorPage: GutenbergEditorPage;
let pricingTableBlock: PricingTableBlock;
let page: Page;
let logoImage: string;
let logoImage: TestFile;

// Test data
const pricingTableBlockPrice = 888;
const heroBlockHeading = 'Hero heading';
const clicktoTweetBlockTweet =
'The foolish man seeks happiness in the distance. The wise grows it under his feet. — James Oppenheim';

setupHooks( ( args ) => {
setupHooks( ( args: { page: Page } ) => {
page = args.page;
} );

beforeAll( async () => {
logoImage = await MediaHelper.createTestImage();
logoImage = await MediaHelper.createTestFile( TEST_IMAGE_PATH );
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All other methods for generating test files have been removed. Test writers are expected to pass the path of the source image file to the createTestFile method.

} );

it( 'Log in', async function () {
Expand Down Expand Up @@ -78,7 +79,7 @@ describe( DataHelper.createSuiteTitle( 'Blocks: CoBlocks' ), () => {
it( `Insert ${ LogosBlock.blockName } block and set image`, async function () {
const blockHandle = await gutenbergEditorPage.addBlock( LogosBlock.blockName );
const logosBlock = new LogosBlock( blockHandle );
await logosBlock.upload( logoImage );
await logosBlock.upload( logoImage.fullpath );
} );

it( 'Publish and visit post', async function () {
Expand All @@ -104,6 +105,6 @@ describe( DataHelper.createSuiteTitle( 'Blocks: CoBlocks' ), () => {
);

it( `Confirm Logos block is visible in published post`, async () => {
await LogosBlock.validatePublishedContent( page, path.parse( logoImage ).name );
await LogosBlock.validatePublishedContent( page, [ logoImage.filename ] );
} );
} );
48 changes: 25 additions & 23 deletions test/e2e/specs/specs-playwright/wp-blocks__media-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* @group gutenberg
*/

import path from 'path';
import {
setupHooks,
DataHelper,
Expand All @@ -14,26 +13,27 @@ import {
ImageBlock,
AudioBlock,
FileBlock,
TestFile,
} from '@automattic/calypso-e2e';
import { Page } from 'playwright';
import { TEST_IMAGE_PATH, TEST_AUDIO_PATH } from '../constants';

describe( DataHelper.createSuiteTitle( 'Blocks: Media (Upload)' ), () => {
describe( DataHelper.createSuiteTitle( 'Blocks: Media (Upload)' ), function () {
let gutenbergEditorPage: GutenbergEditorPage;
let page: Page;
let testFiles: { image: string; image_reserved_name: string; audio: string };
let testFiles: { image: TestFile; image_reserved_name: TestFile; audio: TestFile };

setupHooks( ( args ) => {
page = args.page;
} );

beforeAll( async () => {
testFiles = {
image: await MediaHelper.createTestImage(),
image_reserved_name: await MediaHelper.createTestFile( {
sourceFileName: 'image0.jpg',
testFileName: 'filewith#?#?reservedurlchars',
image: await MediaHelper.createTestFile( TEST_IMAGE_PATH ),
image_reserved_name: await MediaHelper.createTestFile( TEST_IMAGE_PATH, {
postfix: 'filewith#?#?reservedurlchars',
} ),
audio: await MediaHelper.createTestAudio(),
audio: await MediaHelper.createTestFile( TEST_AUDIO_PATH ),
};
} );

Expand All @@ -55,47 +55,49 @@ describe( DataHelper.createSuiteTitle( 'Blocks: Media (Upload)' ), () => {
it( `${ ImageBlock.blockName } block: upload image file`, async function () {
const blockHandle = await gutenbergEditorPage.addBlock( ImageBlock.blockName );
const imageBlock = new ImageBlock( blockHandle );
await imageBlock.upload( testFiles.image );
await imageBlock.upload( testFiles.image.fullpath );
} );

it( `${ ImageBlock.blockName } block: upload image file with reserved URL characters`, async function () {
const blockHandle = await gutenbergEditorPage.addBlock( ImageBlock.blockName );
const imageBlock = new ImageBlock( blockHandle );
await imageBlock.upload( testFiles.image_reserved_name );
await imageBlock.upload( testFiles.image_reserved_name.fullpath );
} );

it( `${ AudioBlock.blockName } block: upload audio file`, async function () {
const blockHandle = await gutenbergEditorPage.addBlock( AudioBlock.blockName );
const audioBlock = new AudioBlock( blockHandle );
await audioBlock.upload( testFiles.audio );
await audioBlock.upload( testFiles.audio.fullpath );
} );

it( `${ FileBlock.blockName } block: upload audio file`, async function () {
const blockHandle = await gutenbergEditorPage.addBlock( FileBlock.blockName );
const fileBlock = new FileBlock( blockHandle );
await fileBlock.upload( testFiles.audio );
await fileBlock.upload( testFiles.audio.fullpath );
} );

it( 'Publish and visit post', async function () {
await gutenbergEditorPage.publish( { visit: true } );
// Must save as draft first to bypass issue with post-publish panel being auto-dismissed
// after publishing. May be related to the following issue?
// See https://github.com/Automattic/wp-calypso/issues/54421.
await gutenbergEditorPage.publish( { visit: true, saveDraft: true } );
} );

it( `Confirm Image block is visible in published post`, async () => {
await ImageBlock.validatePublishedContent( page, path.parse( testFiles.image ).name );
it( `Confirm Image block is visible in published post`, async function () {
await ImageBlock.validatePublishedContent( page, [ testFiles.image.filename ] );
} );

it( `Confirm Image block is visible in published post (reserved name)`, async () => {
await ImageBlock.validatePublishedContent(
page,
path.parse( testFiles.image_reserved_name ).name.replace( /[^a-zA-Z ]/g, '' )
);
it( `Confirm Image block is visible in published post (reserved name)`, async function () {
await ImageBlock.validatePublishedContent( page, [
testFiles.image_reserved_name.filename.replace( /[^a-zA-Z ]/g, '' ),
] );
} );

it( `Confirm Audio block is visible in published post`, async () => {
it( `Confirm Audio block is visible in published post`, async function () {
await AudioBlock.validatePublishedContent( page );
} );

it( `Confirm File block is visible in published post`, async () => {
await FileBlock.validatePublishedContent( page, path.parse( testFiles.audio ).name );
it( `Confirm File block is visible in published post`, async function () {
await FileBlock.validatePublishedContent( page, [ testFiles.audio.filename ] );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,29 @@ import {
MediaPage,
SidebarComponent,
setupHooks,
TestFile,
} from '@automattic/calypso-e2e';
import { Page } from 'playwright';
import { TEST_IMAGE_PATH } from '../constants';

describe( DataHelper.createSuiteTitle( 'Media: Edit Media' ), function () {
let testImage;
let page;
let testImage: TestFile;
let page: Page;

setupHooks( ( args ) => {
page = args.page;
} );

beforeAll( async () => {
testImage = await MediaHelper.createTestImage();
testImage = await MediaHelper.createTestFile( TEST_IMAGE_PATH );
} );

describe.each`
siteType | user
${ 'Simple' } | ${ 'defaultUser' }
${ 'Atomic' } | ${ 'wooCommerceUser' }
`( 'Edit Image ($siteType)', function ( { user } ) {
let mediaPage;
let mediaPage: MediaPage;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file has been changed to TypeScript.


it( 'Log In', async function () {
const loginFlow = new LoginFlow( page, user );
Expand All @@ -51,7 +54,7 @@ describe( DataHelper.createSuiteTitle( 'Media: Edit Media' ), function () {
it( 'Upload image', async function () {
// Ideally, we'd not want to upload an image (that's a separate test)
// but occasionally, the photo gallery is cleaned out leaving no images.
const uploadedImageHandle = await mediaPage.upload( testImage );
const uploadedImageHandle = await mediaPage.upload( testImage.fullpath );
const isVisible = await uploadedImageHandle.isVisible();
expect( isVisible ).toBe( true );
} );
Expand Down
Loading