tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

interface.ts (5623B)


      1 /**
      2 * @license
      3 * Copyright 2022 Google Inc.
      4 * SPDX-License-Identifier: Apache-2.0
      5 */
      6 
      7 import Mocha from 'mocha';
      8 import commonInterface from 'mocha/lib/interfaces/common';
      9 import {
     10  setLogCapture,
     11  getCapturedLogs,
     12 } from 'puppeteer-core/internal/common/Debug.js';
     13 
     14 import {testIdMatchesExpectationPattern} from './utils.js';
     15 
     16 type SuiteFunction = ((this: Mocha.Suite) => void) | undefined;
     17 type ExclusiveSuiteFunction = (this: Mocha.Suite) => void;
     18 
     19 const skippedTests: Array<{testIdPattern: string; skip: true}> = process.env[
     20  'PUPPETEER_SKIPPED_TEST_CONFIG'
     21 ]
     22  ? JSON.parse(process.env['PUPPETEER_SKIPPED_TEST_CONFIG'])
     23  : [];
     24 
     25 const deflakeRetries = Number(
     26  process.env['PUPPETEER_DEFLAKE_RETRIES']
     27    ? process.env['PUPPETEER_DEFLAKE_RETRIES']
     28    : 100,
     29 );
     30 const deflakeTestPattern: string | undefined =
     31  process.env['PUPPETEER_DEFLAKE_TESTS'];
     32 
     33 function shouldSkipTest(test: Mocha.Test): boolean {
     34  // TODO: more efficient lookup.
     35  const definition = skippedTests.find(skippedTest => {
     36    return testIdMatchesExpectationPattern(test, skippedTest.testIdPattern);
     37  });
     38  if (definition && definition.skip) {
     39    return true;
     40  }
     41  return false;
     42 }
     43 
     44 function shouldDeflakeTest(test: Mocha.Test): boolean {
     45  if (deflakeTestPattern) {
     46    // TODO: cache if we have seen it already
     47    return testIdMatchesExpectationPattern(test, deflakeTestPattern);
     48  }
     49  return false;
     50 }
     51 
     52 function dumpLogsIfFail(this: Mocha.Context) {
     53  if (this.currentTest?.state === 'failed') {
     54    console.log(
     55      `\n"${this.currentTest.fullTitle()}" failed. Here is a debug log:`,
     56    );
     57    console.log(getCapturedLogs().join('\n') + '\n');
     58  }
     59  setLogCapture(false);
     60 }
     61 
     62 function customBDDInterface(suite: Mocha.Suite) {
     63  const suites: [Mocha.Suite] = [suite];
     64 
     65  suite.on(
     66    Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE,
     67    function (context, file, mocha) {
     68      const common = commonInterface(suites, context, mocha);
     69 
     70      context['before'] = common.before;
     71      context['after'] = common.after;
     72      context['beforeEach'] = common.beforeEach;
     73      context['afterEach'] = common.afterEach;
     74      if (mocha.options.delay) {
     75        context['run'] = common.runWithSuite(suite);
     76      }
     77      function describe(title: string, fn: SuiteFunction) {
     78        return common.suite.create({
     79          title: title,
     80          file: file,
     81          fn: fn,
     82        });
     83      }
     84      describe.only = function (title: string, fn: ExclusiveSuiteFunction) {
     85        return common.suite.only({
     86          title: title,
     87          file: file,
     88          fn: fn,
     89          isOnly: true,
     90        });
     91      };
     92 
     93      describe.skip = function (title: string, fn: SuiteFunction) {
     94        return common.suite.skip({
     95          title: title,
     96          file: file,
     97          fn: fn,
     98        });
     99      };
    100 
    101      describe.withDebugLogs = function (
    102        description: string,
    103        body: (this: Mocha.Suite) => void,
    104      ): void {
    105        context['describe']('with Debug Logs', () => {
    106          context['beforeEach'](() => {
    107            setLogCapture(true);
    108          });
    109          context['afterEach'](dumpLogsIfFail);
    110          context['describe'](description, body);
    111        });
    112      };
    113 
    114      // @ts-expect-error override the method to support custom functionality
    115      context['describe'] = describe;
    116 
    117      function it(title: string, fn: Mocha.TestFunction, itOnly = false) {
    118        const suite = suites[0]! as Mocha.Suite;
    119        const test = new Mocha.Test(title, suite.isPending() ? undefined : fn);
    120        test.file = file;
    121        test.parent = suite;
    122 
    123        const describeOnly = Boolean(
    124          // @ts-expect-error pokes at internal methods
    125          suite.parent?._onlySuites.find(child => {
    126            return child === suite;
    127          }),
    128        );
    129        if (shouldDeflakeTest(test)) {
    130          const deflakeSuit = Mocha.Suite.create(suite, 'with Debug Logs');
    131          test.file = file;
    132          deflakeSuit.beforeEach(function () {
    133            setLogCapture(true);
    134          });
    135          deflakeSuit.afterEach(dumpLogsIfFail);
    136          for (let i = 0; i < deflakeRetries; i++) {
    137            deflakeSuit.addTest(test.clone());
    138          }
    139          return test;
    140        } else if (!(itOnly || describeOnly) && shouldSkipTest(test)) {
    141          const test = new Mocha.Test(title);
    142          test.file = file;
    143          suite.addTest(test);
    144          return test;
    145        } else {
    146          suite.addTest(test);
    147          return test;
    148        }
    149      }
    150 
    151      it.only = function (title: string, fn: Mocha.TestFunction) {
    152        return common.test.only(
    153          mocha,
    154          (context['it'] as unknown as typeof it)(title, fn, true),
    155        );
    156      };
    157 
    158      it.skip = function (title: string) {
    159        return context['it'](title);
    160      };
    161 
    162      function wrapDeflake(
    163        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
    164        func: Function,
    165      ): (repeats: number, title: string, fn: Mocha.AsyncFunc) => void {
    166        return (repeats: number, title: string, fn: Mocha.AsyncFunc): void => {
    167          (context['describe'] as unknown as typeof describe).withDebugLogs(
    168            'with Debug Logs',
    169            () => {
    170              for (let i = 1; i <= repeats; i++) {
    171                func(`${i}/${title}`, fn);
    172              }
    173            },
    174          );
    175        };
    176      }
    177 
    178      it.deflake = wrapDeflake(it);
    179      it.deflakeOnly = wrapDeflake(it.only);
    180 
    181      // @ts-expect-error override the method to support custom functionality
    182      context.it = it;
    183    },
    184  );
    185 }
    186 
    187 customBDDInterface.description = 'Custom BDD';
    188 
    189 module.exports = customBDDInterface;