-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add opt-in usage tracking for blocks added to post content (#2140)
- Loading branch information
Showing
12 changed files
with
356 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { connect } from 'react-redux'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { Button } from '@wordpress/components'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import './style.scss'; | ||
import { bumpStat } from '../utils/tracking'; | ||
import { removeNotice } from '../actions'; | ||
|
||
export const TRACKING_PROMPT_NOTICE_ID = 'notice:enable-tracking-prompt'; | ||
|
||
export function EnableTrackingPrompt( props ) { | ||
function dismissTrackingPrompt( enableTracking ) { | ||
window.setUserSetting( | ||
'gutenberg_tracking', | ||
enableTracking ? 'on' : 'off' | ||
); | ||
if ( enableTracking ) { | ||
bumpStat( 'tracking', 'opt-in' ); | ||
} | ||
props.removeNotice( TRACKING_PROMPT_NOTICE_ID ); | ||
} | ||
|
||
return ( | ||
<div className="enable-tracking-prompt"> | ||
<div className="enable-tracking-prompt__message"> | ||
{ __( 'Can Gutenberg collect data about your usage of the editor?' ) } | ||
<div className="enable-tracking-prompt__buttons"> | ||
<Button | ||
isPrimary | ||
isSmall | ||
onClick={ () => dismissTrackingPrompt( true ) } | ||
> | ||
{ __( 'Yes' ) } | ||
</Button> | ||
<Button | ||
isSecondary | ||
isSmall | ||
onClick={ () => dismissTrackingPrompt( false ) } | ||
> | ||
{ __( 'No' ) } | ||
</Button> | ||
</div> | ||
</div> | ||
<div className="enable-tracking-prompt__clarification"> | ||
{ __( 'Usage data is completely anonymous, does not include your post content, and will only be used to improve the editor.' ) } | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default connect( | ||
undefined, | ||
{ removeNotice } | ||
)( EnableTrackingPrompt ); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
.enable-tracking-prompt { | ||
.enable-tracking-prompt__message { | ||
font-weight: bold; | ||
margin-top: 8px; | ||
|
||
.enable-tracking-prompt__buttons { | ||
float: right; | ||
|
||
.button { | ||
margin-left: 6px; | ||
} | ||
} | ||
} | ||
|
||
.enable-tracking-prompt__clarification { | ||
clear: both; | ||
margin-bottom: 8px; | ||
font-size: 90%; | ||
font-style: italic; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { mount } from 'enzyme'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { | ||
EnableTrackingPrompt, | ||
TRACKING_PROMPT_NOTICE_ID, | ||
} from '../'; | ||
|
||
describe( 'EnableTrackingPrompt', () => { | ||
const tracking = require( '../../utils/tracking' ); // no default export | ||
const originalSetUserSetting = window.setUserSetting; | ||
const originalBumpStat = tracking.bumpStat; | ||
let removeNotice; | ||
|
||
beforeEach( () => { | ||
window.setUserSetting = jest.fn(); | ||
tracking.bumpStat = jest.fn(); | ||
removeNotice = jest.fn(); | ||
} ); | ||
|
||
afterEach( () => { | ||
window.setUserSetting = originalSetUserSetting; | ||
tracking.bumpStat = originalBumpStat; | ||
} ); | ||
|
||
it( 'should render a prompt with Yes and No buttons', () => { | ||
const prompt = mount( | ||
<EnableTrackingPrompt /> | ||
); | ||
const buttons = prompt.find( '.button' ); | ||
expect( buttons.length ).toBe( 2 ); | ||
expect( buttons.at( 0 ).text() ).toBe( 'Yes' ); | ||
expect( buttons.at( 1 ).text() ).toBe( 'No' ); | ||
|
||
expect( window.setUserSetting ) | ||
.not.toHaveBeenCalled(); | ||
expect( tracking.bumpStat ) | ||
.not.toHaveBeenCalled(); | ||
expect( removeNotice ) | ||
.not.toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'should enable tracking when clicking Yes', () => { | ||
const prompt = mount( | ||
<EnableTrackingPrompt removeNotice={ removeNotice } /> | ||
); | ||
const buttonYes = prompt.find( '.button' ) | ||
.filterWhere( node => node.text() === 'Yes' ); | ||
buttonYes.simulate( 'click' ); | ||
|
||
expect( window.setUserSetting ) | ||
.toHaveBeenCalledWith( 'gutenberg_tracking', 'on' ); | ||
expect( tracking.bumpStat ) | ||
.toHaveBeenCalledWith( 'tracking', 'opt-in' ); | ||
expect( removeNotice ) | ||
.toHaveBeenCalledWith( TRACKING_PROMPT_NOTICE_ID ); | ||
} ); | ||
|
||
it( 'should disable tracking when clicking No', () => { | ||
const prompt = mount( | ||
<EnableTrackingPrompt removeNotice={ removeNotice } /> | ||
); | ||
const buttonNo = prompt.find( '.button' ) | ||
.filterWhere( node => node.text() === 'No' ); | ||
buttonNo.simulate( 'click' ); | ||
|
||
expect( window.setUserSetting ) | ||
.toHaveBeenCalledWith( 'gutenberg_tracking', 'off' ); | ||
expect( tracking.bumpStat ) | ||
.not.toHaveBeenCalled(); | ||
expect( removeNotice ) | ||
.toHaveBeenCalledWith( TRACKING_PROMPT_NOTICE_ID ); | ||
} ); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* eslint-disable no-console */ | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { bumpStat } from '../tracking'; | ||
|
||
describe( 'bumpStat', () => { | ||
const originalConsoleError = console.error; | ||
const originalGetUserSetting = window.getUserSetting; | ||
|
||
beforeEach( () => { | ||
console.error = jest.fn(); | ||
window.getUserSetting = () => 'off'; | ||
} ); | ||
|
||
afterEach( () => { | ||
console.error = originalConsoleError; | ||
window.getUserSetting = originalGetUserSetting; | ||
} ); | ||
|
||
it( 'should reject non-string stat groups', () => { | ||
expect( bumpStat( 42, 'valid-name' ) ).toBeUndefined(); | ||
expect( console.error ).toHaveBeenCalledWith( | ||
'Stat group names and stat names must be strings.' | ||
); | ||
} ); | ||
|
||
it( 'should reject non-string stat names', () => { | ||
expect( bumpStat( 'valid_group', 42 ) ).toBeUndefined(); | ||
expect( console.error ).toHaveBeenCalledWith( | ||
'Stat group names and stat names must be strings.' | ||
); | ||
} ); | ||
|
||
it( 'should reject group names with invalid characters', () => { | ||
expect( bumpStat( 'invalid-group', 'valid-name' ) ).toBeUndefined(); | ||
expect( console.error ).toHaveBeenCalledWith( | ||
'Stat group names must consist of letters, numbers, and underscores.' | ||
); | ||
} ); | ||
|
||
it( 'should reject group names longer than 22 chars', () => { | ||
expect( bumpStat( Array( 23 + 1 ).join( 'x' ), 'valid-name' ) ).toBeUndefined(); | ||
expect( console.error ).toHaveBeenCalledWith( | ||
'Stat group names cannot be longer than 22 characters.' | ||
); | ||
} ); | ||
|
||
it( 'should reject stat names with invalid characters', () => { | ||
expect( bumpStat( 'group', 'invalidName' ) ).toBeUndefined(); | ||
expect( console.error ).toHaveBeenCalledWith( | ||
'Stat names must consist of letters, numbers, underscores, and dashes.' | ||
); | ||
} ); | ||
|
||
it( 'should reject stat names longer than 32 chars', () => { | ||
expect( bumpStat( 'name', Array( 33 + 1 ).join( 'x' ) ) ).toBeUndefined(); | ||
expect( console.error ).toHaveBeenCalledWith( | ||
'Stat names cannot be longer than 32 characters.' | ||
); | ||
} ); | ||
|
||
it( 'should do nothing if the user has not opted in', () => { | ||
expect( bumpStat( 'valid_group', 'valid-name' ) ).toBeUndefined(); | ||
expect( console.error ).not.toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'should bump valid stats', () => { | ||
window.getUserSetting = () => 'on'; | ||
const url = bumpStat( 'valid_group', 'valid-name' ); | ||
// There are a couple of pieces of the URL where we don't care about | ||
// testing the specific value. Replace them with placeholders. | ||
const urlMatch = url | ||
.replace( /^[a-z]+:/, 'PROTOCOL:' ) | ||
.replace( /t=[0-9.]+$/, 't=NUMBER' ); | ||
expect( urlMatch ).toBe( | ||
'PROTOCOL://pixel.wp.com/g.gif?v=wpcom-no-pv' | ||
+ '&x_gutenberg_valid_group=valid-name' | ||
+ '&t=NUMBER' | ||
); | ||
} ); | ||
} ); |
Oops, something went wrong.