(core) Update failing tests

Summary:
Test suites may now optionally set a "tutorial" flag to
automatically scaffold a sample tutorial document before
the tests run, and tear it down after.

This is used to fix a failing staging deployment test due
to a mismatch between local and staging deployments; local
deployments did not have a sample tutorial document set up,
while staging/prod deployments did.

Test Plan: Existing tests.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D4354
This commit is contained in:
George Gevoian 2024-09-19 00:07:27 -04:00
parent e97a45143f
commit ce0c912a18
7 changed files with 82 additions and 71 deletions

Binary file not shown.

BIN
test/fixtures/docs/Grist Basics.grist vendored Normal file

Binary file not shown.

View File

@ -4,7 +4,7 @@ import {setupTestSuite} from 'test/nbrowser/testUtils';
describe('BehavioralPrompts', function() { describe('BehavioralPrompts', function() {
this.timeout(20000); this.timeout(20000);
const cleanup = setupTestSuite(); const cleanup = setupTestSuite({tutorial: true});
let session: gu.Session; let session: gu.Session;
let docId: string; let docId: string;
@ -228,16 +228,19 @@ describe('BehavioralPrompts', function() {
}); });
describe('when in a tutorial', function() { describe('when in a tutorial', function() {
gu.withEnvironmentSnapshot({'GRIST_UI_FEATURES': 'tutorials'}); gu.withEnvironmentSnapshot({
'GRIST_UI_FEATURES': 'tutorials',
'GRIST_TEMPLATE_ORG': 'templates',
'GRIST_ONBOARDING_TUTORIAL_DOC_ID': 'grist-basics',
});
before(async () => { before(async () => {
const tutorialSession = await gu.session().user('user3').login({ const tutorialSession = await gu.session().user('user3').login({
showTips: true, showTips: true,
}); });
const doc = await tutorialSession.tempDoc(cleanup, 'DocTutorial.grist', {load: false}); await tutorialSession.loadDocMenu('/');
const api = tutorialSession.createHomeApi(); await driver.find('.test-dm-basic-tutorial').click();
await api.updateDoc(doc.id, {type: 'tutorial'}); await gu.waitForDocToLoad();
await tutorialSession.loadDoc(`/doc/${doc.id}`);
}); });
it('should not be shown', async function() { it('should not be shown', async function() {

View File

@ -1,47 +1,34 @@
import {DocCreationInfo} from 'app/common/DocListAPI';
import {UserAPI} from 'app/common/UserAPI'; import {UserAPI} from 'app/common/UserAPI';
import {assert, driver, Key} from 'mocha-webdriver'; import {assert, driver, Key} from 'mocha-webdriver';
import * as gu from 'test/nbrowser/gristUtils'; import * as gu from 'test/nbrowser/gristUtils';
import {server, setupTestSuite} from 'test/nbrowser/testUtils'; import {setupTestSuite} from 'test/nbrowser/testUtils';
import {EnvironmentSnapshot} from 'test/server/testUtils';
describe('DocTutorial', function () { describe('DocTutorial', function () {
this.timeout(60000); this.timeout(60000);
gu.bigScreen(); gu.bigScreen();
let doc: DocCreationInfo;
let api: UserAPI; let api: UserAPI;
let ownerSession: gu.Session; let ownerSession: gu.Session;
let editorSession: gu.Session; let editorSession: gu.Session;
let viewerSession: gu.Session; let viewerSession: gu.Session;
let oldEnv: EnvironmentSnapshot;
const cleanup = setupTestSuite({samples: true, team: true}); setupTestSuite({samples: true, tutorial: true, team: true});
gu.withEnvironmentSnapshot({
'GRIST_UI_FEATURES': 'tutorials',
'GRIST_TEMPLATE_ORG': 'templates',
'GRIST_ONBOARDING_TUTORIAL_DOC_ID': 'grist-basics',
});
before(async () => { before(async () => {
ownerSession = await gu.session().customTeamSite('templates').user('support').login(); ownerSession = await gu.session().customTeamSite('templates').user('support').login();
doc = await ownerSession.tempDoc(cleanup, 'DocTutorial.grist', {load: false});
oldEnv = new EnvironmentSnapshot();
process.env.GRIST_UI_FEATURES = 'tutorials';
process.env.GRIST_TEMPLATE_ORG = 'templates';
process.env.GRIST_ONBOARDING_TUTORIAL_DOC_ID = doc.id;
await server.restart();
api = ownerSession.createHomeApi(); api = ownerSession.createHomeApi();
await api.updateDoc(doc.id, {type: 'tutorial'}); await api.updateDocPermissions('grist-basics', {users: {
await api.updateDocPermissions(doc.id, {users: {
'anon@getgrist.com': 'viewers',
'everyone@getgrist.com': 'viewers',
[gu.translateUser('user1').email]: 'editors', [gu.translateUser('user1').email]: 'editors',
}}); }});
}); });
after(async function () {
oldEnv.restore();
await server.restart();
});
describe('when logged out', function () { describe('when logged out', function () {
before(async () => { before(async () => {
viewerSession = await gu.session().anon.login(); viewerSession = await gu.session().anon.login();
@ -93,15 +80,14 @@ describe('DocTutorial', function () {
it('shows a popup containing slides generated from the GristDocTutorial table', async function() { it('shows a popup containing slides generated from the GristDocTutorial table', async function() {
assert.isTrue(await driver.findWait('.test-doc-tutorial-popup', 2000).isDisplayed()); assert.isTrue(await driver.findWait('.test-doc-tutorial-popup', 2000).isDisplayed());
assert.equal(await driver.find('.test-floating-popup-header').getText(), 'DocTutorial'); assert.equal(await driver.find('.test-floating-popup-header').getText(), 'Grist Basics');
assert.equal( assert.equal(
await driver.findWait('.test-doc-tutorial-popup h1', 2000).getText(), await driver.findWait('.test-doc-tutorial-popup h1', 2000).getText(),
'Intro' 'Intro'
); );
assert.equal( assert.match(
await driver.find('.test-doc-tutorial-popup p').getText(), await driver.find('.test-doc-tutorial-popup').getText(),
'Welcome to the Grist Basics tutorial. We will cover the most important Grist ' /Welcome to the Grist Basics tutorial/
+ 'concepts and features. Lets get started.'
); );
}); });
@ -260,7 +246,7 @@ describe('DocTutorial', function () {
it('does not show the GristDocTutorial page or table to non-editors', async function() { it('does not show the GristDocTutorial page or table to non-editors', async function() {
viewerSession = await gu.session().customTeamSite('templates').user('user2').login(); viewerSession = await gu.session().customTeamSite('templates').user('user2').login();
await viewerSession.loadDoc(`/doc/${doc.id}`); await viewerSession.loadDoc(`/doc/grist-basics`);
assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2']); assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2']);
await driver.find('.test-tools-raw').click(); await driver.find('.test-tools-raw').click();
await driver.findWait('.test-raw-data-list', 1000); await driver.findWait('.test-raw-data-list', 1000);
@ -279,7 +265,7 @@ describe('DocTutorial', function () {
it('only allows users access to their own forks', async function() { it('only allows users access to their own forks', async function() {
await driver.navigate().to(forkUrl); await driver.navigate().to(forkUrl);
assert.match(await driver.findWait('.test-error-header', 2000).getText(), /Access denied/); assert.match(await driver.findWait('.test-error-header', 2000).getText(), /Access denied/);
await viewerSession.loadDoc(`/doc/${doc.id}`); await viewerSession.loadDoc(`/doc/grist-basics`);
let otherForkUrl: string; let otherForkUrl: string;
await driver.wait(async () => { await driver.wait(async () => {
otherForkUrl = await driver.getCurrentUrl(); otherForkUrl = await driver.getCurrentUrl();
@ -303,8 +289,8 @@ describe('DocTutorial', function () {
); );
assert.equal( assert.equal(
await driver.find('.test-doc-tutorial-popup h1 + p').getText(), await driver.find('.test-doc-tutorial-popup h1 + p').getText(),
'On the left-side panel is a list of pages which are views of your data. Right' 'On the left-side panel is a list of pages, which are views of your data. ' +
+ ' now, there are two pages, Page 1 and Page 2. You are looking at Page 1.' 'Right now, there are two pages: Page 1 and Page 2. You are looking at Page 1.'
); );
assert.isTrue(await driver.find('.test-doc-tutorial-popup-next').isDisplayed()); assert.isTrue(await driver.find('.test-doc-tutorial-popup-next').isDisplayed());
assert.isTrue(await driver.find('.test-doc-tutorial-popup-previous').isDisplayed()); assert.isTrue(await driver.find('.test-doc-tutorial-popup-previous').isDisplayed());
@ -314,10 +300,9 @@ describe('DocTutorial', function () {
await driver.find('.test-doc-tutorial-popup h1').getText(), await driver.find('.test-doc-tutorial-popup h1').getText(),
'Intro' 'Intro'
); );
assert.equal( assert.match(
await driver.find('.test-doc-tutorial-popup p').getText(), await driver.find('.test-doc-tutorial-popup').getText(),
'Welcome to the Grist Basics tutorial. We will cover the most important Grist ' /Welcome to the Grist Basics tutorial/
+ 'concepts and features. Lets get started.'
); );
assert.isTrue(await driver.find('.test-doc-tutorial-popup-next').isDisplayed()); assert.isTrue(await driver.find('.test-doc-tutorial-popup-next').isDisplayed());
assert.isFalse(await driver.find('.test-doc-tutorial-popup-previous').isDisplayed()); assert.isFalse(await driver.find('.test-doc-tutorial-popup-previous').isDisplayed());
@ -339,8 +324,7 @@ describe('DocTutorial', function () {
); );
assert.equal( assert.equal(
await driver.find('.test-doc-tutorial-popup p').getText(), await driver.find('.test-doc-tutorial-popup p').getText(),
"You can add new columns to your table by clicking the + icon" "Let's start with the basics of working with spreadsheet data — columns and rows."
+ ' to the far right of your column headers.'
); );
const slide1 = await driver.find('.test-doc-tutorial-popup-slide-1'); const slide1 = await driver.find('.test-doc-tutorial-popup-slide-1');
@ -355,10 +339,9 @@ describe('DocTutorial', function () {
await driver.find('.test-doc-tutorial-popup h1').getText(), await driver.find('.test-doc-tutorial-popup h1').getText(),
'Intro' 'Intro'
); );
assert.equal( assert.match(
await driver.find('.test-doc-tutorial-popup p').getText(), await driver.find('.test-doc-tutorial-popup').getText(),
'Welcome to the Grist Basics tutorial. We will cover the most important Grist ' /Welcome to the Grist Basics tutorial/
+ 'concepts and features. Lets get started.'
); );
}); });
@ -367,7 +350,7 @@ describe('DocTutorial', function () {
assert.isTrue(await driver.find('.test-doc-tutorial-lightbox').isDisplayed()); assert.isTrue(await driver.find('.test-doc-tutorial-lightbox').isDisplayed());
assert.equal( assert.equal(
await driver.find('.test-doc-tutorial-lightbox-image').getAttribute('src'), await driver.find('.test-doc-tutorial-lightbox-image').getAttribute('src'),
'https://www.getgrist.com/wp-content/uploads/2023/03/Row-1-Intro.png' 'https://www.getgrist.com/wp-content/uploads/2023/11/Row-1-Intro.png'
); );
await driver.find('.test-doc-tutorial-lightbox-close').click(); await driver.find('.test-doc-tutorial-lightbox-close').click();
assert.isFalse(await driver.find('.test-doc-tutorial-lightbox').isPresent()); assert.isFalse(await driver.find('.test-doc-tutorial-lightbox').isPresent());
@ -421,8 +404,7 @@ describe('DocTutorial', function () {
); );
assert.equal( assert.equal(
await driver.find('.test-doc-tutorial-popup p').getText(), await driver.find('.test-doc-tutorial-popup p').getText(),
"You can add new columns to your table by clicking the + icon" "Let's start with the basics of working with spreadsheet data — columns and rows."
+ ' to the far right of your column headers.'
); );
}); });
@ -431,7 +413,7 @@ describe('DocTutorial', function () {
await gu.getCell(0, 1).click(); await gu.getCell(0, 1).click();
await gu.sendKeys('Redacted', Key.ENTER); await gu.sendKeys('Redacted', Key.ENTER);
await gu.waitForServer(); await gu.waitForServer();
await editorSession.loadDoc(`/doc/${doc.id}`); await editorSession.loadDoc(`/doc/grist-basics`);
let currentUrl: string; let currentUrl: string;
await driver.wait(async () => { await driver.wait(async () => {
currentUrl = await driver.getCurrentUrl(); currentUrl = await driver.getCurrentUrl();
@ -455,7 +437,7 @@ describe('DocTutorial', function () {
it('skips starting or resuming a tutorial if the open mode is set to default', async function() { it('skips starting or resuming a tutorial if the open mode is set to default', async function() {
ownerSession = await gu.session().customTeamSite('templates').user('support').login(); ownerSession = await gu.session().customTeamSite('templates').user('support').login();
await ownerSession.loadDoc(`/doc/${doc.id}/m/default`); await ownerSession.loadDoc(`/doc/grist-basics/m/default`);
assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2', 'GristDocTutorial']); assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2', 'GristDocTutorial']);
await driver.find('.test-tools-raw').click(); await driver.find('.test-tools-raw').click();
await gu.waitForServer(); await gu.waitForServer();
@ -492,10 +474,9 @@ describe('DocTutorial', function () {
await driver.findWait('.test-doc-tutorial-popup h1', 2000).getText(), await driver.findWait('.test-doc-tutorial-popup h1', 2000).getText(),
'Intro' 'Intro'
); );
assert.equal( assert.match(
await driver.find('.test-doc-tutorial-popup p').getText(), await driver.find('.test-doc-tutorial-popup').getText(),
'Welcome to the Grist Basics tutorial. We will cover the most important Grist ' /Welcome to the Grist Basics tutorial/
+ 'concepts and features. Lets get started.'
); );
// Check that edits were reset. // Check that edits were reset.
@ -509,7 +490,7 @@ describe('DocTutorial', function () {
it('allows owners to replace original', async function() { it('allows owners to replace original', async function() {
ownerSession = await gu.session().customTeamSite('templates').user('support').login(); ownerSession = await gu.session().customTeamSite('templates').user('support').login();
await ownerSession.loadDoc(`/doc/${doc.id}`); await ownerSession.loadDoc(`/doc/grist-basics`);
// Make an edit to one of the tutorial slides. // Make an edit to one of the tutorial slides.
await gu.openPage('GristDocTutorial'); await gu.openPage('GristDocTutorial');
@ -537,7 +518,7 @@ describe('DocTutorial', function () {
// Switch to another user and restart the tutorial. // Switch to another user and restart the tutorial.
viewerSession = await gu.session().customTeamSite('templates').user('user2').login(); viewerSession = await gu.session().customTeamSite('templates').user('user2').login();
await viewerSession.loadDoc(`/doc/${doc.id}`); await viewerSession.loadDoc(`/doc/grist-basics`);
await driver.findWait('.test-doc-tutorial-popup-restart', 2000).click(); await driver.findWait('.test-doc-tutorial-popup-restart', 2000).click();
await driver.find('.test-modal-confirm').click(); await driver.find('.test-modal-confirm').click();
await gu.waitForServer(); await gu.waitForServer();
@ -553,7 +534,7 @@ describe('DocTutorial', function () {
it('redirects to the last visited site when finished', async function() { it('redirects to the last visited site when finished', async function() {
const otherSiteSession = await gu.session().personalSite.user('user1').addLogin(); const otherSiteSession = await gu.session().personalSite.user('user1').addLogin();
await otherSiteSession.loadDocMenu('/'); await otherSiteSession.loadDocMenu('/');
await ownerSession.loadDoc(`/doc/${doc.id}`); await ownerSession.loadDoc(`/doc/grist-basics`);
await driver.findWait('.test-doc-tutorial-popup-slide-13', 2000).click(); await driver.findWait('.test-doc-tutorial-popup-slide-13', 2000).click();
await driver.find('.test-doc-tutorial-popup-next').click(); await driver.find('.test-doc-tutorial-popup-next').click();
await gu.waitForDocMenuToLoad(); await gu.waitForDocMenuToLoad();
@ -572,9 +553,9 @@ describe('DocTutorial', function () {
describe('without tutorial flag set', function () { describe('without tutorial flag set', function () {
before(async () => { before(async () => {
await api.updateDoc(doc.id, {type: null}); await api.updateDoc('grist-basics', {type: null});
ownerSession = await gu.session().customTeamSite('templates').user('support').login(); ownerSession = await gu.session().customTeamSite('templates').user('support').login();
await ownerSession.loadDoc(`/doc/${doc.id}`); await ownerSession.loadDoc(`/doc/grist-basics`);
}); });
afterEach(() => gu.checkForErrors()); afterEach(() => gu.checkForErrors());
@ -584,10 +565,9 @@ describe('DocTutorial', function () {
['Page 1', 'Page 2', 'GristDocTutorial', 'Table1']); ['Page 1', 'Page 2', 'GristDocTutorial', 'Table1']);
await gu.openPage('GristDocTutorial'); await gu.openPage('GristDocTutorial');
assert.deepEqual( assert.deepEqual(
await gu.getVisibleGridCells({cols: [1, 2], rowNums: [1]}), await gu.getVisibleGridCells({cols: [1], rowNums: [1]}),
[ [
'# Intro\n\nWelcome to the Grist Basics tutorial V2.', '# Intro\n\nWelcome to the Grist Basics tutorial V2.',
'',
] ]
); );
await driver.find('.test-tools-raw').click(); await driver.find('.test-tools-raw').click();

View File

@ -9,8 +9,12 @@ import {server, setupTestSuite} from 'test/nbrowser/testUtils';
describe('HomeIntro', function() { describe('HomeIntro', function() {
this.timeout(40000); this.timeout(40000);
setupTestSuite({samples: true}); setupTestSuite({samples: true, tutorial: true});
gu.withEnvironmentSnapshot({'GRIST_TEMPLATE_ORG': 'templates'}); gu.withEnvironmentSnapshot({
'GRIST_UI_FEATURES': 'templates,tutorials',
'GRIST_TEMPLATE_ORG': 'templates',
'GRIST_ONBOARDING_TUTORIAL_DOC_ID': 'grist-basics',
});
describe("Anonymous on merged-org", function() { describe("Anonymous on merged-org", function() {
it('should show welcome for anonymous user', async function() { it('should show welcome for anonymous user', async function() {
@ -94,7 +98,7 @@ describe('HomeIntro', function() {
assert.isTrue(await driver.find('.test-intro-cards').isDisplayed()); assert.isTrue(await driver.find('.test-intro-cards').isDisplayed());
assert.isTrue(await driver.find('.test-intro-video-tour').isDisplayed()); assert.isTrue(await driver.find('.test-intro-video-tour').isDisplayed());
assert.isFalse(await driver.find('.test-intro-tutorial').isDisplayed()); assert.isTrue(await driver.find('.test-intro-tutorial').isDisplayed());
assert.isTrue(await driver.find('.test-intro-create-doc').isDisplayed()); assert.isTrue(await driver.find('.test-intro-create-doc').isDisplayed());
assert.isTrue(await driver.find('.test-intro-import-doc').isDisplayed()); assert.isTrue(await driver.find('.test-intro-import-doc').isDisplayed());
assert.isTrue(await driver.find('.test-intro-templates').isDisplayed()); assert.isTrue(await driver.find('.test-intro-templates').isDisplayed());
@ -276,6 +280,7 @@ async function testEmptyWorkspace(options = { buttons: false }) {
// Check that we don't see empty info. // Check that we don't see empty info.
await driver.navigate().back(); await driver.navigate().back();
await gu.waitForDocMenuToLoad(); await gu.waitForDocMenuToLoad();
await gu.dismissBehavioralPrompts();
assert.equal(await driver.find('.test-empty-workspace-info').isPresent(), false); assert.equal(await driver.find('.test-empty-workspace-info').isPresent(), false);
// Remove created document, it also checks that document is visible. // Remove created document, it also checks that document is visible.
await deleteFirstDoc(); await deleteFirstDoc();

View File

@ -2667,7 +2667,7 @@ export async function addSupportUserIfPossible() {
/** /**
* Adds samples to the Examples & Templates page. * Adds samples to the Examples & Templates page.
*/ */
async function addSamples() { async function addSamples(includeTutorial: boolean) {
await addSupportUserIfPossible(); await addSupportUserIfPossible();
const homeApi = createHomeApi('support', 'docs'); const homeApi = createHomeApi('support', 'docs');
@ -2734,6 +2734,27 @@ async function addSamples() {
'anon@getgrist.com': 'viewers', 'anon@getgrist.com': 'viewers',
}}); }});
} }
if (includeTutorial) {
await templatesApi.newWorkspace({name: 'Tutorials'}, 'current');
const tutorialDocId = (await importFixturesDoc('support', 'templates', 'Tutorials',
'Grist Basics.grist', {load: false, newName: 'Grist Basics.grist'})).id;
await templatesApi.updateDoc(
tutorialDocId,
{
type: 'tutorial',
options: {
description: 'Learn Grist fast with a hands-on tutorial that covers the basics.',
icon: 'https://grist-static.com/icons/grist-basics.png',
},
urlId: 'grist-basics',
},
);
await homeApi.updateDocPermissions(tutorialDocId, {users: {
'everyone@getgrist.com': 'viewers',
'anon@getgrist.com': 'viewers',
}});
}
} }
/** /**
@ -2749,9 +2770,9 @@ function removeTemplatesOrg() {
* "Examples & Templates" page in before(), and remove added samples * "Examples & Templates" page in before(), and remove added samples
* in after(). * in after().
*/ */
export function addSamplesForSuite() { export function addSamplesForSuite(includeTutorial = false) {
before(async function() { before(async function() {
await addSamples(); await addSamples(includeTutorial);
}); });
after(async function() { after(async function() {

View File

@ -92,6 +92,7 @@ setOptionsModifyFunc(({chromeOpts, firefoxOpts}) => {
interface TestSuiteOptions { interface TestSuiteOptions {
samples?: boolean; samples?: boolean;
tutorial?: boolean;
team?: boolean; team?: boolean;
// If set, clear user preferences for all test users at the end of the suite. It should be used // If set, clear user preferences for all test users at the end of the suite. It should be used
@ -268,10 +269,11 @@ export function setupCleanup() {
*/ */
export function setupRequirement(options: TestSuiteOptions) { export function setupRequirement(options: TestSuiteOptions) {
const cleanup = setupCleanup(); const cleanup = setupCleanup();
if (options.samples) { const {samples, tutorial} = options;
if (samples || tutorial) {
if (process.env.TEST_ADD_SAMPLES || !server.isExternalServer()) { if (process.env.TEST_ADD_SAMPLES || !server.isExternalServer()) {
gu.shareSupportWorkspaceForSuite(); // TODO: Remove after the support workspace is removed from the backend. gu.shareSupportWorkspaceForSuite(); // TODO: Remove after the support workspace is removed from the backend.
gu.addSamplesForSuite(); gu.addSamplesForSuite(tutorial);
} }
} }