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

Block Editor Tracking: Add pattern_category property to wpcom_pattern_inserted event #53492

Merged
20 changes: 19 additions & 1 deletion apps/wpcom-block-editor/src/wpcom/features/tracking.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,29 @@ const getBlocksTracker = ( eventName ) => ( blockIds ) => {
*/
const maybeTrackPatternInsertion = ( actionData ) => {
const meta = find( actionData, ( item ) => item?.patternName );
const patternName = meta?.patternName;
let patternName = meta?.patternName;

// Quick block inserter doesn't use an object to store the patternName
// in the metadata. The pattern name is just directly used as a string.
if ( ! patternName ) {
const patterns = select( 'core/block-editor' ).getSettings().__experimentalBlockPatterns;
const actionDataToCheck = Object.values( actionData ).filter(
( data ) => typeof data === 'string'
);
const foundPattern = patterns.find( ( pattern ) => actionDataToCheck.includes( pattern.name ) );
if ( foundPattern ) {
patternName = foundPattern.name;
}
}

if ( patternName ) {
const patternCategory =
// Pattern category dropdown in global inserter
document.querySelector( '.block-editor-inserter__panel-header-patterns select' )?.value;

tracksRecordEvent( 'wpcom_pattern_inserted', {
pattern_name: patternName,
pattern_category: patternCategory,
blocks_replaced: actionData?.blocks_replaced,
} );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ if ( typeof window !== 'undefined' ) {
// Enable a events stack for e2e testing purposes
// on e2e test environments only.
// see https://github.com/Automattic/wp-calypso/pull/41329.
const E2E_STACK_SIZE = 20;
const E2E_STACK_SIZE = 100;
if ( isE2ETest() ) {
e2ETracksDebug( 'E2E env' );
window._e2eEventsStack = [];
Expand Down
99 changes: 85 additions & 14 deletions test/e2e/lib/components/site-editor-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { By } from 'selenium-webdriver';
* Internal dependencies
*/
import * as driverHelper from '../driver-helper';
import * as driverManager from '../driver-manager';
import AsyncBaseContainer from '../async-base-container';
import GutenbergEditorComponent from '../gutenberg/gutenberg-editor-component';

Expand Down Expand Up @@ -59,6 +60,23 @@ export default class SiteEditorComponent extends AsyncBaseContainer {
} );
}

async isBlockInserterOpen() {
const inserterMenuLocator = By.css( '.block-editor-inserter__menu' );
return await driverHelper.isElementLocated( this.driver, inserterMenuLocator );
}

async openBlockInserter() {
const inserterToggleLocator = By.css(
'.edit-site-header .edit-site-header-toolbar__inserter-toggle'
);
if ( ! ( await this.isBlockInserterOpen() ) ) {
await driverHelper.clickWhenClickable( this.driver, inserterToggleLocator );
}

const inserterMenuLocator = By.css( '.block-editor-inserter__menu' );
await driverHelper.waitUntilElementLocatedAndVisible( this.driver, inserterMenuLocator );
}

async openBlockInserterAndSearch( searchTerm ) {
await this.runInCanvas( async () => {
await driverHelper.scrollIntoView(
Expand All @@ -67,23 +85,43 @@ export default class SiteEditorComponent extends AsyncBaseContainer {
'start'
);
} );
const inserterToggleLocator = By.css(
'.edit-site-header .edit-site-header-toolbar__inserter-toggle'
);
const inserterMenuLocator = By.css( '.block-editor-inserter__menu' );

await this.openBlockInserter();
const inserterSearchInputLocator = By.css( 'input.block-editor-inserter__search-input' );
await driverHelper.setWhenSettable( this.driver, inserterSearchInputLocator, searchTerm );
}

if ( await driverHelper.isElementNotLocated( this.driver, inserterMenuLocator ) ) {
await driverHelper.clickWhenClickable( this.driver, inserterToggleLocator );
// "Click" twice - the first click seems to trigger a tooltip, the second opens the menu
// See https://github.com/Automattic/wp-calypso/issues/43179
if ( await driverHelper.isElementNotLocated( this.driver, inserterMenuLocator ) ) {
await driverHelper.clickWhenClickable( this.driver, inserterToggleLocator );
}
async insertPattern( category, name ) {
await this.openBlockInserter();

await driverHelper.waitUntilElementLocatedAndVisible( this.driver, inserterMenuLocator );
}
await driverHelper.setWhenSettable( this.driver, inserterSearchInputLocator, searchTerm );
const patternTabLocator = By.css(
'.block-editor-inserter__tabs .components-tab-panel__tabs-item[id$="patterns"]'
);
const patternCategoryDropdownLocator = By.css(
'.components-tab-panel__tab-content .components-select-control__input'
);
const patternCategoryDropdownOptionLocator = By.css(
`.components-tab-panel__tab-content .components-select-control__input option[value="${ category }"]`
);
const patternItemLocator = By.css(
`.block-editor-block-patterns-list__list-item[aria-label="${ name }"]`
);
await driverHelper.clickWhenClickable( this.driver, patternTabLocator );
await driverHelper.clickWhenClickable( this.driver, patternCategoryDropdownLocator );
await driverHelper.clickWhenClickable( this.driver, patternCategoryDropdownOptionLocator );
await driverHelper.clickWhenClickable( this.driver, patternCategoryDropdownLocator );
await driverHelper.clickWhenClickable( this.driver, patternItemLocator );
}

async closeBlockInserter() {
const inserterCloseLocator = By.css(
driverManager.currentScreenSize() === 'mobile'
? '.edit-site-editor__inserter-panel-header .components-button'
: '.edit-site-header-toolbar__inserter-toggle'
);
const inserterMenuLocator = By.css( '.block-editor-inserter__menu' );
await driverHelper.clickWhenClickable( this.driver, inserterCloseLocator );
await driverHelper.waitUntilElementNotLocated( this.driver, inserterMenuLocator );
}

async addBlock( title ) {
Expand Down Expand Up @@ -179,4 +217,37 @@ export default class SiteEditorComponent extends AsyncBaseContainer {
);
}
}

async dismissNotices() {
const snackbarNoticeLocator = By.css(
'.components-snackbar[aria-label="Dismiss this notice"]'
);

const notices = await this.driver.findElements( snackbarNoticeLocator );
for ( const notice of notices ) {
await driverHelper.clickWhenClickable( this.driver, () => notice );
}

await driverHelper.waitUntilElementNotLocated( this.driver, snackbarNoticeLocator );
}

async insertBlockOrPatternViaBlockAppender( name, container = 'Group' ) {
const containerBlockId = await this.addBlock( container );
await this.runInCanvas( async () => {
const blockAppenderLocator = By.css(
`#${ containerBlockId } .block-editor-button-block-appender`
);
await driverHelper.clickWhenClickable( this.driver, blockAppenderLocator );
} );

const quickInserterSearchInputLocator = By.css(
'.block-editor-inserter__quick-inserter .block-editor-inserter__search-input'
);
const patternItemLocator = By.css(
'.block-editor-inserter__quick-inserter .block-editor-block-types-list__item, .block-editor-inserter__quick-inserter .block-editor-block-patterns-list__item'
);

await driverHelper.setWhenSettable( this.driver, quickInserterSearchInputLocator, name );
await driverHelper.clickWhenClickable( this.driver, patternItemLocator );
}
}
80 changes: 66 additions & 14 deletions test/e2e/lib/gutenberg/gutenberg-editor-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,31 +230,58 @@ export default class GutenbergEditorComponent extends AsyncBaseContainer {
return await driverHelper.isElementLocated( this.driver, By.css( '.block-editor-warning' ) );
}

async isBlockInserterOpen() {
const inserterMenuLocator = By.css( '.block-editor-inserter__menu' );
return await driverHelper.isElementLocated( this.driver, inserterMenuLocator );
}

async openBlockInserter() {
const inserterToggleLocator = By.css(
'.edit-post-header .edit-post-header-toolbar__inserter-toggle'
);
if ( ! ( await this.isBlockInserterOpen() ) ) {
await driverHelper.clickWhenClickable( this.driver, inserterToggleLocator );
}

const inserterMenuLocator = By.css( '.block-editor-inserter__menu' );
await driverHelper.waitUntilElementLocatedAndVisible( this.driver, inserterMenuLocator );
}

async openBlockInserterAndSearch( searchTerm ) {
await driverHelper.scrollIntoView(
this.driver,
By.css( '.block-editor-writing-flow' ),
'start'
);
const inserterToggleLocator = By.css(
'.edit-post-header .edit-post-header-toolbar__inserter-toggle'
);
const inserterMenuLocator = By.css( '.block-editor-inserter__menu' );
const inserterSearchInputLocator = By.css( 'input.block-editor-inserter__search-input' );

if ( await driverHelper.isElementNotLocated( this.driver, inserterMenuLocator ) ) {
await driverHelper.clickWhenClickable( this.driver, inserterToggleLocator );
// "Click" twice - the first click seems to trigger a tooltip, the second opens the menu
// See https://github.com/Automattic/wp-calypso/issues/43179
if ( await driverHelper.isElementNotLocated( this.driver, inserterMenuLocator ) ) {
await driverHelper.clickWhenClickable( this.driver, inserterToggleLocator );
}
await this.openBlockInserter();
const inserterSearchInputLocator = By.css( 'input.block-editor-inserter__search-input' );

await driverHelper.waitUntilElementLocatedAndVisible( this.driver, inserterMenuLocator );
}
await driverHelper.setWhenSettable( this.driver, inserterSearchInputLocator, searchTerm );
}

async insertPattern( category, name ) {
await this.openBlockInserter();

const patternTabLocator = By.css(
'.block-editor-inserter__tabs .components-tab-panel__tabs-item[id$="patterns"]'
);
const patternCategoryDropdownLocator = By.css(
'.components-tab-panel__tab-content .components-select-control__input'
);
const patternCategoryDropdownOptionLocator = By.css(
`.components-tab-panel__tab-content .components-select-control__input option[value="${ category }"]`
);
const patternItemLocator = By.css(
`.block-editor-block-patterns-list__list-item[aria-label="${ name }"]`
);
await driverHelper.clickWhenClickable( this.driver, patternTabLocator );
await driverHelper.clickWhenClickable( this.driver, patternCategoryDropdownLocator );
await driverHelper.clickWhenClickable( this.driver, patternCategoryDropdownOptionLocator );
await driverHelper.clickWhenClickable( this.driver, patternCategoryDropdownLocator );
await driverHelper.clickWhenClickable( this.driver, patternItemLocator );
}

// @TODO: Remove `.block-editor-inserter__results .components-panel__body-title` selector in favor of the `.block-editor-inserter__block-list .block-editor-inserter__panel-title` selector when Gutenberg 8.0.0 is deployed.
async isBlockCategoryPresent( name ) {
const categoryLocator =
Expand Down Expand Up @@ -676,4 +703,29 @@ export default class GutenbergEditorComponent extends AsyncBaseContainer {
By.css( '.edit-post-header .table-of-contents button' )
);
}

async dismissNotices() {
const locator = By.css( '.components-snackbar[aria-label="Dismiss this notice"]' );
const notices = await this.driver.findElements( locator );
await Promise.all( notices.map( ( notice ) => notice.click() ) );
await driverHelper.waitUntilElementNotLocated( this.driver, locator );
}

async insertBlockOrPatternViaBlockAppender( name, container = 'Group' ) {
const containerBlockId = await this.addBlock( container );
const blockAppenderLocator = By.css(
`#${ containerBlockId } .block-editor-button-block-appender`
);
await driverHelper.clickWhenClickable( this.driver, blockAppenderLocator );

const quickInserterSearchInputLocator = By.css(
'.block-editor-inserter__quick-inserter .block-editor-inserter__search-input'
);
const patternItemLocator = By.css(
'.block-editor-inserter__quick-inserter .block-editor-block-types-list__item, .block-editor-inserter__quick-inserter .block-editor-block-patterns-list__item'
);

await driverHelper.setWhenSettable( this.driver, quickInserterSearchInputLocator, name );
await driverHelper.clickWhenClickable( this.driver, patternItemLocator );
}
}
95 changes: 94 additions & 1 deletion test/e2e/lib/gutenberg/tracking/general-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { By } from 'selenium-webdriver';
*/
import GutenbergEditorComponent from '../gutenberg-editor-component';
import SiteEditorComponent from '../../components/site-editor-component';
import { getEventsStack, getTotalEventsFiredForBlock } from './utils';
import { clearEventsStack, getEventsStack, getTotalEventsFiredForBlock } from './utils';
import * as driverHelper from '../../driver-helper';

export function createGeneralTests( { it, editorType, postType } ) {
Expand Down Expand Up @@ -173,6 +173,99 @@ export function createGeneralTests( { it, editorType, postType } ) {
);
} );

it( 'Tracks "wpcom_pattern_inserted"', async function () {
const editor = await EditorComponent.Expect( this.driver, gutenbergEditorType );

await editor.insertPattern( 'list', 'List with Image' );
const eventsStackList = await getEventsStack( this.driver );
await clearEventsStack( this.driver );

await editor.insertPattern( 'gallery', 'Heading and Three Images' );
// We need to save the eventsStack after each insertion to make sure we
// aren't running out of the E2E queue size.
const eventsStackGallery = await getEventsStack( this.driver );
Addison-Stavlo marked this conversation as resolved.
Show resolved Hide resolved
if ( await editor.isBlockInserterOpen() ) {
await editor.closeBlockInserter();
}
await editor.dismissNotices();

const patternInsertedEvents = [ ...eventsStackGallery, ...eventsStackList ].filter(
( [ eventName ] ) => eventName === 'wpcom_pattern_inserted'
);
assert.strictEqual(
patternInsertedEvents.length,
2,
'"wpcom_pattern_inserted" editor tracking event failed to fire for both patterns'
);
const [ , eventDataGallery ] = patternInsertedEvents[ 0 ];
const [ , eventDataList ] = patternInsertedEvents[ 1 ];
assert.strictEqual(
eventDataGallery.pattern_name,
'a8c/heading-and-three-images',
'"wpcom_pattern_inserted" editor tracking event pattern name property is incorrect'
);
assert.strictEqual(
eventDataGallery.pattern_category,
'gallery',
'"wpcom_pattern_inserted" editor tracking event pattern category property is incorrect'
);
assert.strictEqual(
eventDataList.pattern_name,
'a8c/list-with-image',
'"wpcom_pattern_inserted" editor tracking event pattern name property is incorrect'
);
assert.strictEqual(
eventDataList.pattern_category,
'list',
'"wpcom_pattern_inserted" editor tracking event pattern category property is incorrect'
);
} );

it( 'Tracks "wpcom_pattern_inserted"', async function () {
const editor = await EditorComponent.Expect( this.driver, gutenbergEditorType );

await editor.insertBlockOrPatternViaBlockAppender( 'List with Image' );
const eventsStackList = await getEventsStack( this.driver );
await clearEventsStack( this.driver );

await editor.insertBlockOrPatternViaBlockAppender( 'Heading and Three Images' );
// We need to save the eventsStack after each insertion to make sure we
// aren't running out of the E2E queue size.
const eventsStackGallery = await getEventsStack( this.driver );
await editor.dismissNotices();

const patternInsertedEvents = [ ...eventsStackGallery, ...eventsStackList ].filter(
( [ eventName ] ) => eventName === 'wpcom_pattern_inserted'
);
assert.strictEqual(
patternInsertedEvents.length,
2,
'"wpcom_pattern_inserted" editor tracking event failed to fire for both patterns'
);
const [ , eventDataGallery ] = patternInsertedEvents[ 0 ];
const [ , eventDataList ] = patternInsertedEvents[ 1 ];
assert.strictEqual(
eventDataGallery.pattern_name,
'a8c/heading-and-three-images',
'"wpcom_pattern_inserted" editor tracking event pattern name property is incorrect'
);
assert.strictEqual(
typeof eventDataGallery.pattern_category,
'undefined',
'"wpcom_pattern_inserted" editor tracking event pattern category property should not be present'
);
assert.strictEqual(
eventDataList.pattern_name,
'a8c/list-with-image',
'"wpcom_pattern_inserted" editor tracking event pattern name property is incorrect'
);
assert.strictEqual(
typeof eventDataGallery.pattern_category,
'undefined',
'"wpcom_pattern_inserted" editor tracking event pattern category property should not be present'
);
} );

if ( editorType === 'post' ) {
it( 'Tracks "wpcom_block_editor_details_open" event', async function () {
const editor = await EditorComponent.Expect( this.driver, gutenbergEditorType );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ describe( `[${ host }] Calypso Gutenberg Site Editor Tracking: (${ screenSize })

await editor.addBlock( 'Template Part' );
await clearEventsStack( this.driver );
await editor.dismissNotices();

await editor.runInCanvas( async () => {
await driverHelper.clickWhenClickable(
Expand Down Expand Up @@ -583,6 +584,7 @@ describe( `[${ host }] Calypso Gutenberg Site Editor Tracking: (${ screenSize })
// so the insert event won't intefere with our asserts.
const blockId = await editor.addBlock( 'Template Part' );
await clearEventsStack( this.driver );
await editor.dismissNotices();

// Add a template part block and select an existing template part.
// Make sure the template part is loaded before moving on.
Expand Down