Skip to content

Commit ef457fd

Browse files
committed
Walk the Gutenberg card through its own words, and test main's strings in the pseudo-locale
The Gutenberg journey had no issue linked, so it never showed the form, the fold or the loop-back. It now links one in the record and walks signed out, signed in with the fold open, and the opened pull request. Unit tests pin the stage labels, the scope refusal, a composed GitHub failure and the plural stale message in the pseudo-locale.
1 parent 11ef362 commit ef457fd

4 files changed

Lines changed: 104 additions & 12 deletions

File tree

‎tests/e2e/journeys/i18n.spec.js‎

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -531,12 +531,11 @@ test( 'the Help menu and the native file dialogs are translated in main', async
531531

532532
test( 'the Open a pull request card is fully translatable, from signing in to the pull request it opened', async ( { session } ) => {
533533
// GitHub is answered at the IPC seam, as in open-pull-request.spec.js.
534-
// What is GitHub's or the checkout's stays as it is and is left out of
535-
// the scan: the code to type, the login, the fork, the branch, the title
536-
// an untitled pull request gets, and the number of the ticket.
534+
// What is GitHub's stays as it is and is left out of the scan: the code
535+
// to type, the fork and the branch, each of which is its own element.
537536
const CODE = 'ABCD-1234';
538537
const BRANCH = 'trac-60001';
539-
const THEIRS = [ CODE, 'janedoe', 'janedoe/wordpress-develop', BRANCH, 'Ticket #60001', 'github.com/login/device' ];
538+
const THEIRS = [ CODE, 'janedoe/wordpress-develop', BRANCH ];
540539
const site = await makeSite( session );
541540
const { app, page } = await session.start( site.settings, { lang: 'en-XA' } );
542541
await app.evaluate( ( { ipcMain } ) => {
@@ -629,17 +628,26 @@ test( 'the Open a pull request card is fully translatable, from signing in to th
629628
expect( ( await inCard() ).filter( ( text ) => text !== link ) ).toEqual( [] );
630629
} );
631630

632-
test( 'the Open a pull request card on a Gutenberg site is fully translatable, signed in', async ( { session } ) => {
633-
// The card's words that are the project's own. The issue is linked in
634-
// the record rather than through its card, which is the Core test's.
631+
test( 'the Open a pull request card on a Gutenberg site is fully translatable, in the words that are Gutenberg\'s own', async ( { session } ) => {
632+
// The card's words that differ by project: the ask when signed out, the
633+
// notes help and the fold with the form, and the loop-back once the pull
634+
// request exists. The issue is linked in the site's record rather than
635+
// through its card, which is the Core test's.
636+
const ISSUE = '71234';
637+
const BRANCH = `fix/issue-${ ISSUE }`;
638+
const THEIRS = [ 'janedoe/gutenberg', BRANCH ];
635639
const site = await makeSite( session );
636-
Object.assign( site.settings.siteMeta[ site.dir ], { projectType: 'gutenberg' } );
640+
Object.assign( site.settings.siteMeta[ site.dir ], { projectType: 'gutenberg', tracTicket: ISSUE } );
637641
write( site.dir, LOGIN, '<?php // my fix\n' );
638642
const { app, page } = await session.start( site.settings, { lang: 'en-XA' } );
639643
await app.evaluate( ( { ipcMain } ) => {
644+
global.__e2eLogin = null;
640645
const answers = {
641646
'git:list-ticket-patches': () => ( { ok: true, prs: { status: 'ok', items: [] } } ),
642-
'github:account': () => ( { ok: true, login: 'janedoe', configured: true, testMode: null } ),
647+
'url:open': () => true,
648+
'github:account': () => ( { ok: true, login: global.__e2eLogin, configured: true, testMode: null } ),
649+
'github:sign-in': () => ( { ok: true, userCode: 'ABCD-1234', verificationUri: 'https://github.com/login/device' } ),
650+
'github:open-pr': () => ( { ok: true, url: 'https://github.com/WordPress/gutenberg/pull/9', number: 9, branch: 'fix/issue-71234', exactBase: true } ),
643651
};
644652
for ( const [ channel, answer ] of Object.entries( answers ) ) {
645653
ipcMain.removeHandler( channel );
@@ -648,7 +656,31 @@ test( 'the Open a pull request card on a Gutenberg site is fully translatable, s
648656
} );
649657
await page.getByRole( 'button', { name: pseudoLocalize( 'Review & submit changes' ), exact: true } ).click();
650658
const card = page.getByRole( 'dialog' ).getByText( pseudoLocalize( 'Open a pull request' ), { exact: true } ).locator( '..' );
651-
await expect( card.getByRole( 'button', { name: 'janedoe/gutenberg', exact: true } ) ).toBeVisible( { timeout: 30_000 } );
652-
await expect( card.getByText( pseudoLocalize( 'No issue is linked to this site. A pull request has to cite one: link it in the GitHub issue card.' ), { exact: true } ) ).toBeVisible();
653-
expect( ( await unwrapped( card ) ).filter( ( text ) => ! [ 'janedoe', 'janedoe/gutenberg' ].includes( text ) ) ).toEqual( [] );
659+
const inCard = async () => ( await unwrapped( card ) ).filter( ( text ) => ! THEIRS.includes( text ) );
660+
const button = ( label ) => card.getByRole( 'button', { name: pseudoLocalize( label ), exact: true } );
661+
662+
// Signed out: what the app cannot do for a contributor on GitHub.
663+
await expect( card.getByText( pseudoLocalize( 'It cannot create the GitHub account for you.' ), { exact: true } ) ).toBeVisible( { timeout: 30_000 } );
664+
expect( await inCard() ).toEqual( [] );
665+
666+
// Signed in, with the form and its fold open.
667+
await button( 'Sign in with GitHub' ).click();
668+
await expect( card.getByText( 'ABCD-1234', { exact: true } ) ).toBeVisible();
669+
await app.evaluate( ( { BrowserWindow } ) => {
670+
global.__e2eLogin = 'janedoe';
671+
BrowserWindow.getAllWindows()[ 0 ].webContents.send( 'github:sign-in:done', { ok: true, login: 'janedoe' } );
672+
} );
673+
await expect( button( 'Open pull request' ) ).toBeVisible();
674+
await card.locator( 'summary' ).click();
675+
await expect( card.getByText( pseudoLocalize( 'How pull requests work in Gutenberg' ), { exact: true } ) ).toBeVisible();
676+
await expect( card.getByText( pseudoLocalize( 'Goes at the top of the description. The Fixes line that links the issue and your WordPress.org username are added underneath.' ), { exact: true } ) ).toBeVisible();
677+
expect( await inCard() ).toEqual( [] );
678+
679+
// The pull request it opened, and the line back to the issue.
680+
await button( 'Open pull request' ).click();
681+
await expect( card.getByText( pseudoLocalize( 'The Fixes line already lists it on the issue. A comment there still tells the people watching it.' ), { exact: true } ) ).toBeVisible();
682+
const opened = card.locator( '.success-text' );
683+
await expect( opened ).toHaveText( /^\[.*\]$/ );
684+
const link = ( await opened.getByRole( 'button' ).textContent() ).trim();
685+
expect( ( await inCard() ).filter( ( text ) => text !== link ) ).toEqual( [] );
654686
} );

‎tests/unit/github-auth.test.cjs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
const test = require('node:test');
1313
const assert = require('node:assert');
1414
const { requestDeviceCode, pollForToken, fetchViewer, getClientId } = require('../../src/github-auth.cjs');
15+
const { addFilter, removeFilter } = require('@wordpress/hooks');
16+
const { pseudoLocalize } = require('../../src/renderer/pseudo-locale.cjs');
1517

1618
const CLIENT_ID = 'Ov23liTEST';
1719

@@ -228,3 +230,14 @@ test('fetchViewer refuses a token whose granted scopes cannot push', async () =>
228230
// No header is no evidence — some token types omit it entirely.
229231
assert.strictEqual((await viewer(undefined)).ok, true);
230232
});
233+
234+
test('a refused scope is said in the locale main applied, around the names it keeps', async (t) => {
235+
// main applies the locale at startup; a message built at require time
236+
// would stay English.
237+
addFilter('i18n.gettext', 'test/pseudo-locale', (text) => pseudoLocalize(text));
238+
t.after(() => removeFilter('i18n.gettext', 'test/pseudo-locale'));
239+
const res = await fetchViewer('gho_test', { get: async () => ({ status: 200, headers: { 'x-oauth-scopes': '' }, json: { login: 'janedoe' } }) });
240+
assert.match(res.error, /^\[.*\]$/);
241+
assert.ok(res.error.includes('"WordPress Contributor Toolkit"'), res.error);
242+
assert.ok(res.error.includes('github.com → Settings → Applications'), res.error);
243+
});

‎tests/unit/github-pr.test.cjs‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const {
3131
openPullRequest
3232
} = require('../../src/github-pr.cjs');
3333
const { citesWorkItemFor } = require('../../src/patch-sources.cjs');
34+
const { addFilter, removeFilter } = require('@wordpress/hooks');
35+
const { pseudoLocalize } = require('../../src/renderer/pseudo-locale.cjs');
3436

3537
const TOKEN = 'gho_test';
3638
const LOGIN = 'janedoe';
@@ -835,3 +837,39 @@ test('openPullRequest refuses an empty change before it touches GitHub', async (
835837
assert.strictEqual(res.reason, 'empty');
836838
assert.deepStrictEqual(api.calls, []);
837839
});
840+
841+
test('main’s failures are said in the locale it applied: the step, the plural and the sentence around GitHub’s words', async (t) => {
842+
addFilter('i18n.gettext', 'test/pseudo-locale', (text) => pseudoLocalize(text));
843+
addFilter('i18n.ngettext', 'test/pseudo-locale', (text) => pseudoLocalize(text));
844+
t.after(() => {
845+
removeFilter('i18n.gettext', 'test/pseudo-locale');
846+
removeFilter('i18n.ngettext', 'test/pseudo-locale');
847+
});
848+
849+
// A step that failed, composed with GitHub's reason, which stays as
850+
// GitHub wrote it.
851+
const branch = await commitAndBranch({ token: TOKEN, login: LOGIN, ticketId: 1, message: 'm', treeSha: 't', parentSha: 'p' }, router({
852+
'POST git/commits': { status: 201, json: { sha: 'commit1' } },
853+
'POST git/refs': { status: 403, headers: { 'x-ratelimit-remaining': '0' }, json: { message: 'API rate limit exceeded' } }
854+
}));
855+
assert.match(branch.error, /^\[/);
856+
assert.ok(branch.error.includes(pseudoLocalize('Could not create the branch')), branch.error);
857+
assert.ok(branch.error.includes('API rate limit exceeded'), branch.error);
858+
859+
// Two files under a moved trunk take the plural.
860+
const stale = await openPullRequest({
861+
token: TOKEN, login: LOGIN, ticketId: 1, baseSha: 'abc123',
862+
files: [
863+
{ path: 'a.php', kind: 'modify', content: Buffer.from('x'), mode: '100644', baseBlobSha: 'blob-old' },
864+
{ path: 'b.php', kind: 'modify', content: Buffer.from('y'), mode: '100644', baseBlobSha: 'blob-old' }
865+
],
866+
title: 't', body: 'b'
867+
}, router({
868+
...happyPathRoutes(),
869+
[`GET ${FORK_URL}/git/ref/heads/trunk`]: { status: 200, json: { object: { sha: 'newer-tip' } } },
870+
'GET contents/a.php?ref=newer-tip': { status: 200, json: { sha: 'blob-upstream' } },
871+
'GET contents/b.php?ref=newer-tip': { status: 200, json: { sha: 'blob-upstream' } }
872+
}));
873+
assert.strictEqual(stale.reason, 'stale');
874+
assert.strictEqual(stale.error, pseudoLocalize('Trunk has moved under files you edited (%s). Update this site to the latest trunk, check your changes still apply, and try again.').replace('%s', 'a.php, b.php'));
875+
});

‎tests/unit/pr-stage.test.cjs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
const test = require('node:test');
44
const assert = require('node:assert');
55
const { prStageLabel } = require('../../src/renderer/pr-stage.cjs');
6+
const { addFilter, removeFilter } = require('@wordpress/hooks');
7+
const { pseudoLocalize } = require('../../src/renderer/pseudo-locale.cjs');
68

79
// The slow step names the repository it forks: the site's, or the sandbox the
810
// override sends the run to (#251). Whatever it is given, it says.
@@ -18,3 +20,10 @@ test('every other stage has a label, and an unknown one still says something', (
1820
assert.strictEqual(prStageLabel('opening', 'WordPress/gutenberg'), 'Opening the pull request…');
1921
assert.strictEqual(prStageLabel('', 'WordPress/gutenberg'), 'Working…');
2022
});
23+
24+
test('the stage labels are translated when they are read, not when the module loads', (t) => {
25+
addFilter('i18n.gettext', 'test/pseudo-locale', (text) => pseudoLocalize(text));
26+
t.after(() => removeFilter('i18n.gettext', 'test/pseudo-locale'));
27+
assert.strictEqual(prStageLabel('forking', 'WordPress/gutenberg'), pseudoLocalize('Creating your fork of %s…').replace('%s', 'WordPress/gutenberg'));
28+
assert.strictEqual(prStageLabel('opening', 'WordPress/gutenberg'), pseudoLocalize('Opening the pull request…'));
29+
});

0 commit comments

Comments
 (0)