From e4d104ee41f3bc0e2794ed09075a1bae29b0159f Mon Sep 17 00:00:00 2001 From: Dmitry S Date: Wed, 13 Mar 2024 12:27:09 -0400 Subject: [PATCH] (core) Try to get Billing tests to pass consistently. Summary: 1. Set pageLoad timeout to 10s (default is 5 minutes) 2. Disable chrome's prompts to save credit card info, which may be affecting Stripe pages 3. Periodically record of logs and screenshots for the most-failing test case so that whenever it fails in a bad way (timeout with no indication of what's wrong), we can hope to find out what's wrong. Test Plan: Planning to celebrate if Billing tests pass. Reviewers: georgegevoian Reviewed By: georgegevoian Subscribers: georgegevoian Differential Revision: https://phab.getgrist.com/D4193 --- test/nbrowser/testUtils.ts | 68 +++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/test/nbrowser/testUtils.ts b/test/nbrowser/testUtils.ts index 020f253f..687dd1a5 100644 --- a/test/nbrowser/testUtils.ts +++ b/test/nbrowser/testUtils.ts @@ -16,9 +16,12 @@ * first-failure for debugging and quick reruns. */ import log from 'app/server/lib/log'; -import {addToRepl, assert, driver, enableDebugCapture, Key, setOptionsModifyFunc, useServer} from 'mocha-webdriver'; +import {addToRepl, assert, driver, enableDebugCapture, ITimeouts, + Key, setOptionsModifyFunc, useServer} from 'mocha-webdriver'; import * as gu from 'test/nbrowser/gristUtils'; import {server} from 'test/nbrowser/testServer'; +import * as path from 'path'; +import * as fs from 'fs/promises'; // Exports the server object with useful methods such as getHost(), waitServerReady(), // simulateLogin(), etc. @@ -72,6 +75,10 @@ setOptionsModifyFunc(({chromeOpts, firefoxOpts}) => { }), "download.default_directory": server.testDir, "savefile.default_directory": server.testDir, + "autofill": { + profile_enabled: false, + credit_card_enabled: false, + }, }); }); @@ -82,6 +89,10 @@ interface TestSuiteOptions { // If set, clear user preferences for all test users at the end of the suite. It should be used // for suites that modify preferences. Not that it only works in dev, not in deployment tests. clearUserPrefs?: boolean; + + // Max milliseconds to wait for a page to finish loading. E.g. affects clicks that cause + // navigation, which wait for that. A navigation that takes longer will throw an exception. + pageLoadTimeout?: number; } // Sets up the test suite to use the Grist server, and also to record logs and screenshots after @@ -122,6 +133,10 @@ export function setupTestSuite(options?: TestSuiteOptions) { // with tests that don't use the same server. after(async () => server.closeDatabase()); + if (options?.pageLoadTimeout) { + setDriverTimeoutsForSuite({pageLoad: options.pageLoadTimeout}); + } + return setupRequirement({team: true, ...options}); } @@ -175,6 +190,21 @@ async function clearTestUserPreferences() { await dbManager.testClearUserPrefs(emails); } +export function setDriverTimeoutsForSuite(newTimeouts: ITimeouts) { + let prevTimeouts: ITimeouts|null = null; + + before(async () => { + prevTimeouts = await driver.manage().getTimeouts(); + await driver.manage().setTimeouts(newTimeouts); + }); + + after(async () => { + if (prevTimeouts) { + await driver.manage().setTimeouts(prevTimeouts); + } + }); +} + export type CleanupFunc = (() => void|Promise); /** @@ -287,3 +317,39 @@ export function setupRequirement(options: TestSuiteOptions) { }); return cleanup; } + +export async function withDriverLogging( + test: Mocha.Runnable|undefined, periodMs: number, timeoutMs: number, + callback: () => Promise +) { + const dir = process.env.MOCHA_WEBDRIVER_LOGDIR!; + assert.isOk(dir, "driverLogging: MOCHA_WEBDRIVER_LOGDIR not set"); + const testName = test?.file ? path.basename(test.file, path.extname(test.file)) : "unnamed"; + const logPath = path.resolve(dir, `${testName}-driverLogging.log`); + await fs.mkdir(dir, {recursive: true}); + + let running = false; + async function repeat() { + if (running) { + console.log("driverLogging: skipping because previous repeat still running"); + return; + } + running = true; + try { + await driver.saveScreenshot(`${testName}-driverLoggingScreenshot-{N}.png`); + const messages = await driver.fetchLogs('driver'); + await fs.appendFile(logPath, messages.join("\n") + "\n"); + } finally { + running = false; + } + } + + const periodic = setInterval(repeat, periodMs); + const timeout = setTimeout(() => clearInterval(periodic), timeoutMs); + try { + return await callback(); + } finally { + clearInterval(periodic); + clearTimeout(timeout); + } +}