Best JavaScript code snippet using playwright-internal
runner.js
Source:runner.js  
...107      console.error('=================');108      console.error(' duplicate test titles are not allowed.');109      for (const [title, tests] of result === null || result === void 0 ? void 0 : result.clashingTests.entries()) {110        console.error(` - title: ${title}`);111        for (const test of tests) console.error(`   - ${buildItemLocation(config.rootDir, test)}`);112        console.error('=================');113      }114    }115    await this._flushOutput();116    return result.status;117  }118  async _flushOutput() {119    // Calling process.exit() might truncate large stdout/stderr output.120    // See https://github.com/nodejs/node/issues/6456.121    // See https://github.com/nodejs/node/issues/12921122    await new Promise(resolve => process.stdout.write('', () => resolve()));123    await new Promise(resolve => process.stderr.write('', () => resolve()));124  }125  async _run(list, testFileReFilters, projectName) {126    const testFileFilter = testFileReFilters.length ? (0, _util2.createMatcher)(testFileReFilters.map(e => e.re)) : () => true;127    const config = this._loader.fullConfig();128    const projects = this._loader.projects().filter(project => {129      return !projectName || project.config.name.toLocaleLowerCase() === projectName.toLocaleLowerCase();130    });131    if (projectName && !projects.length) {132      const names = this._loader.projects().map(p => p.config.name).filter(name => !!name);133      if (!names.length) throw new Error(`No named projects are specified in the configuration file`);134      throw new Error(`Project "${projectName}" not found. Available named projects: ${names.map(name => `"${name}"`).join(', ')}`);135    }136    const files = new Map();137    const allTestFiles = new Set();138    for (const project of projects) {139      const testDir = project.config.testDir;140      if (!fs.existsSync(testDir)) throw new Error(`${testDir} does not exist`);141      if (!fs.statSync(testDir).isDirectory()) throw new Error(`${testDir} is not a directory`);142      const allFiles = await collectFiles(project.config.testDir);143      const testMatch = (0, _util2.createMatcher)(project.config.testMatch);144      const testIgnore = (0, _util2.createMatcher)(project.config.testIgnore);145      const testFileExtension = file => ['.js', '.ts', '.mjs'].includes(path.extname(file));146      const testFiles = allFiles.filter(file => !testIgnore(file) && testMatch(file) && testFileFilter(file) && testFileExtension(file));147      files.set(project, testFiles);148      testFiles.forEach(file => allTestFiles.add(file));149    }150    const webServer = config.webServer && (await _webServer.WebServer.create(config.webServer));151    let globalSetupResult;152    if (config.globalSetup) globalSetupResult = await (await this._loader.loadGlobalHook(config.globalSetup, 'globalSetup'))(this._loader.fullConfig());153    try {154      var _this$_reporter$onBeg2, _this$_reporter3, _this$_reporter$onEnd3, _this$_reporter5;155      for (const file of allTestFiles) await this._loader.loadTestFile(file);156      const preprocessRoot = new _test.Suite('');157      for (const fileSuite of this._loader.fileSuites().values()) preprocessRoot._addSuite(fileSuite);158      if (config.forbidOnly) {159        const onlyTestsAndSuites = preprocessRoot._getOnlyItems();160        if (onlyTestsAndSuites.length > 0) {161          const locations = onlyTestsAndSuites.map(testOrSuite => {162            // Skip root and file.163            const title = testOrSuite.titlePath().slice(2).join(' ');164            return `${buildItemLocation(config.rootDir, testOrSuite)} > ${title}`;165          });166          return {167            status: 'forbid-only',168            locations169          };170        }171      }172      const clashingTests = getClashingTestsPerSuite(preprocessRoot);173      if (clashingTests.size > 0) return {174        status: 'clashing-test-titles',175        clashingTests: clashingTests176      };177      filterOnly(preprocessRoot);178      filterByFocusedLine(preprocessRoot, testFileReFilters);179      const fileSuites = new Map();180      for (const fileSuite of preprocessRoot.suites) fileSuites.set(fileSuite._requireFile, fileSuite);181      const outputDirs = new Set();182      const grepMatcher = (0, _util2.createMatcher)(config.grep);183      const grepInvertMatcher = config.grepInvert ? (0, _util2.createMatcher)(config.grepInvert) : null;184      const rootSuite = new _test.Suite('');185      for (const project of projects) {186        const projectSuite = new _test.Suite(project.config.name);187        rootSuite._addSuite(projectSuite);188        for (const file of files.get(project)) {189          const fileSuite = fileSuites.get(file);190          if (!fileSuite) continue;191          for (let repeatEachIndex = 0; repeatEachIndex < project.config.repeatEach; repeatEachIndex++) {192            const cloned = project.cloneFileSuite(fileSuite, repeatEachIndex, test => {193              const grepTitle = test.titlePath().join(' ');194              if (grepInvertMatcher !== null && grepInvertMatcher !== void 0 && grepInvertMatcher(grepTitle)) return false;195              return grepMatcher(grepTitle);196            });197            if (cloned) projectSuite._addSuite(cloned);198          }199        }200        outputDirs.add(project.config.outputDir);201      }202      let total = rootSuite.allTests().length;203      if (!total) return {204        status: 'no-tests'205      };206      await Promise.all(Array.from(outputDirs).map(outputDir => removeFolderAsync(outputDir).catch(e => {})));207      let testGroups = createTestGroups(rootSuite);208      const shard = config.shard;209      if (shard) {210        const shardGroups = [];211        const shardTests = new Set(); // Each shard gets some tests.212        const shardSize = Math.floor(total / shard.total); // First few shards get one more test each.213        const extraOne = total - shardSize * shard.total;214        const currentShard = shard.current - 1; // Make it zero-based for calculations.215        const from = shardSize * currentShard + Math.min(extraOne, currentShard);216        const to = from + shardSize + (currentShard < extraOne ? 1 : 0);217        let current = 0;218        for (const group of testGroups) {219          // Any test group goes to the shard that contains the first test of this group.220          // So, this shard gets any group that starts at [from; to)221          if (current >= from && current < to) {222            shardGroups.push(group);223            for (const test of group.tests) shardTests.add(test);224          }225          current += group.tests.length;226        }227        testGroups = shardGroups;228        filterSuite(rootSuite, () => false, test => shardTests.has(test));229        total = rootSuite.allTests().length;230      }231      if (process.stdout.isTTY) {232        console.log();233        const jobs = Math.min(config.workers, testGroups.length);234        const shardDetails = shard ? `, shard ${shard.current} of ${shard.total}` : '';235        console.log(`Running ${total} test${total > 1 ? 's' : ''} using ${jobs} worker${jobs > 1 ? 's' : ''}${shardDetails}`);236      }237      let sigint = false;238      let sigintCallback;239      const sigIntPromise = new Promise(f => sigintCallback = f);240      const sigintHandler = () => {241        // We remove the handler so that second Ctrl+C immediately kills the runner242        // via the default sigint handler. This is handy in the case where our shutdown243        // takes a lot of time or is buggy.244        //245        // When running through NPM we might get multiple SIGINT signals246        // for a single Ctrl+C - this is an NPM bug present since at least NPM v6.247        // https://github.com/npm/cli/issues/1591248        // https://github.com/npm/cli/issues/2124249        //250        // Therefore, removing the handler too soon will just kill the process251        // with default handler without printing the results.252        // We work around this by giving NPM 1000ms to send us duplicate signals.253        // The side effect is that slow shutdown or bug in our runner will force254        // the user to hit Ctrl+C again after at least a second.255        setTimeout(() => process.off('SIGINT', sigintHandler), 1000);256        sigint = true;257        sigintCallback();258      };259      process.on('SIGINT', sigintHandler);260      (_this$_reporter$onBeg2 = (_this$_reporter3 = this._reporter).onBegin) === null || _this$_reporter$onBeg2 === void 0 ? void 0 : _this$_reporter$onBeg2.call(_this$_reporter3, config, rootSuite);261      this._didBegin = true;262      let hasWorkerErrors = false;263      if (!list) {264        const dispatcher = new _dispatcher.Dispatcher(this._loader, testGroups, this._reporter);265        await Promise.race([dispatcher.run(), sigIntPromise]);266        await dispatcher.stop();267        hasWorkerErrors = dispatcher.hasWorkerErrors();268      }269      if (sigint) {270        var _this$_reporter$onEnd2, _this$_reporter4;271        await ((_this$_reporter$onEnd2 = (_this$_reporter4 = this._reporter).onEnd) === null || _this$_reporter$onEnd2 === void 0 ? void 0 : _this$_reporter$onEnd2.call(_this$_reporter4, {272          status: 'interrupted'273        }));274        return {275          status: 'sigint'276        };277      }278      const failed = hasWorkerErrors || rootSuite.allTests().some(test => !test.ok());279      await ((_this$_reporter$onEnd3 = (_this$_reporter5 = this._reporter).onEnd) === null || _this$_reporter$onEnd3 === void 0 ? void 0 : _this$_reporter$onEnd3.call(_this$_reporter5, {280        status: failed ? 'failed' : 'passed'281      }));282      return {283        status: failed ? 'failed' : 'passed'284      };285    } finally {286      if (globalSetupResult && typeof globalSetupResult === 'function') await globalSetupResult(this._loader.fullConfig());287      if (config.globalTeardown) await (await this._loader.loadGlobalHook(config.globalTeardown, 'globalTeardown'))(this._loader.fullConfig());288      await (webServer === null || webServer === void 0 ? void 0 : webServer.kill());289    }290  }291}292exports.Runner = Runner;293function filterOnly(suite) {294  const suiteFilter = suite => suite._only;295  const testFilter = test => test._only;296  return filterSuite(suite, suiteFilter, testFilter);297}298function filterByFocusedLine(suite, focusedTestFileLines) {299  const testFileLineMatches = (testFileName, testLine) => focusedTestFileLines.some(({300    re,301    line302  }) => {303    re.lastIndex = 0;304    return re.test(testFileName) && (line === testLine || line === null);305  });306  const suiteFilter = suite => !!suite.location && testFileLineMatches(suite.location.file, suite.location.line);307  const testFilter = test => testFileLineMatches(test.location.file, test.location.line);308  return filterSuite(suite, suiteFilter, testFilter);309}310function filterSuite(suite, suiteFilter, testFilter) {311  const onlySuites = suite.suites.filter(child => filterSuite(child, suiteFilter, testFilter) || suiteFilter(child));312  const onlyTests = suite.tests.filter(testFilter);313  const onlyEntries = new Set([...onlySuites, ...onlyTests]);314  if (onlyEntries.size) {315    suite.suites = onlySuites;316    suite.tests = onlyTests;317    suite._entries = suite._entries.filter(e => onlyEntries.has(e)); // Preserve the order.318    return true;319  }320  return false;321}322async function collectFiles(testDir) {323  const checkIgnores = (entryPath, rules, isDirectory, parentStatus) => {324    let status = parentStatus;325    for (const rule of rules) {326      const ruleIncludes = rule.negate;327      if (status === 'included' === ruleIncludes) continue;328      const relative = path.relative(rule.dir, entryPath);329      if (rule.match('/' + relative) || rule.match(relative)) {330        // Matches "/dir/file" or "dir/file"331        status = ruleIncludes ? 'included' : 'ignored';332      } else if (isDirectory && (rule.match('/' + relative + '/') || rule.match(relative + '/'))) {333        // Matches "/dir/subdir/" or "dir/subdir/" for directories.334        status = ruleIncludes ? 'included' : 'ignored';335      } else if (isDirectory && ruleIncludes && (rule.match('/' + relative, true) || rule.match(relative, true))) {336        // Matches "/dir/donotskip/" when "/dir" is excluded, but "!/dir/donotskip/file" is included.337        status = 'ignored-but-recurse';338      }339    }340    return status;341  };342  const files = [];343  const visit = async (dir, rules, status) => {344    const entries = await readDirAsync(dir, {345      withFileTypes: true346    });347    entries.sort((a, b) => a.name.localeCompare(b.name));348    const gitignore = entries.find(e => e.isFile() && e.name === '.gitignore');349    if (gitignore) {350      const content = await readFileAsync(path.join(dir, gitignore.name), 'utf8');351      const newRules = content.split(/\r?\n/).map(s => {352        s = s.trim();353        if (!s) return; // Use flipNegate, because we handle negation ourselves.354        const rule = new _minimatch.Minimatch(s, {355          matchBase: true,356          dot: true,357          flipNegate: true358        });359        if (rule.comment) return;360        rule.dir = dir;361        return rule;362      }).filter(rule => !!rule);363      rules = [...rules, ...newRules];364    }365    for (const entry of entries) {366      if (entry === gitignore || entry.name === '.' || entry.name === '..') continue;367      if (entry.isDirectory() && entry.name === 'node_modules') continue;368      const entryPath = path.join(dir, entry.name);369      const entryStatus = checkIgnores(entryPath, rules, entry.isDirectory(), status);370      if (entry.isDirectory() && entryStatus !== 'ignored') await visit(entryPath, rules, entryStatus);else if (entry.isFile() && entryStatus === 'included') files.push(entryPath);371    }372  };373  await visit(testDir, [], 'included');374  return files;375}376function getClashingTestsPerSuite(rootSuite) {377  function visit(suite, clashingTests) {378    for (const childSuite of suite.suites) visit(childSuite, clashingTests);379    for (const test of suite.tests) {380      const fullTitle = test.titlePath().slice(2).join(' ');381      if (!clashingTests.has(fullTitle)) clashingTests.set(fullTitle, []);382      clashingTests.set(fullTitle, clashingTests.get(fullTitle).concat(test));383    }384  }385  const out = new Map();386  for (const fileSuite of rootSuite.suites) {387    const clashingTests = new Map();388    visit(fileSuite, clashingTests);389    for (const [title, tests] of clashingTests.entries()) {390      if (tests.length > 1) out.set(title, tests);391    }392  }393  return out;394}395function buildItemLocation(rootDir, testOrSuite) {396  if (!testOrSuite.location) return '';397  return `${path.relative(rootDir, testOrSuite.location.file)}:${testOrSuite.location.line}`;398}399function createTestGroups(rootSuite) {400  // This function groups tests that can be run together.401  // Tests cannot be run together when:402  // - They belong to different projects - requires different workers.403  // - They have a different repeatEachIndex - requires different workers.404  // - They have a different set of worker fixtures in the pool - requires different workers.405  // - They have a different requireFile - reuses the worker, but runs each requireFile separately.406  // We try to preserve the order of tests when they require different workers407  // by ordering different worker hashes sequentially.408  const workerHashToOrdinal = new Map();409  const requireFileToOrdinal = new Map();...LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!
