summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/tools/sort-test-expectations.mjs
blob: 972d2448740a2aaab6b7d89b68e1b2f3fdccd3e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
 * @license
 * Copyright 2023 Google Inc.
 * SPDX-License-Identifier: Apache-2.0
 */

// TODO: this could be an eslint rule probably.
import fs from 'fs';
import path from 'path';
import url from 'url';

import prettier from 'prettier';

const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
const source = 'test/TestExpectations.json';
let testExpectations = JSON.parse(fs.readFileSync(source, 'utf-8'));
const committedExpectations = structuredClone(testExpectations);

function testIdMatchesExpectationPattern(title, pattern) {
  const patternRegExString = pattern
    // Replace `*` with non special character
    .replace(/\*/g, '--STAR--')
    // Escape special characters https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
    .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
    // Replace placeholder with greedy match
    .replace(/--STAR--/g, '(.*)?');
  // Match beginning and end explicitly
  const patternRegEx = new RegExp(`^${patternRegExString}$`);
  return patternRegEx.test(title);
}

const prettierConfig = await import(
  path.join(__dirname, '..', '.prettierrc.cjs')
);

function getSpecificity(item) {
  return (
    item.parameters.length +
    (item.testIdPattern.includes('*')
      ? item.testIdPattern === '*'
        ? 0
        : 1
      : 2)
  );
}

testExpectations.sort((a, b) => {
  const result = getSpecificity(a) - getSpecificity(b);
  if (result === 0) {
    return a.testIdPattern.localeCompare(b.testIdPattern);
  }
  return result;
});

testExpectations.forEach(item => {
  item.parameters.sort();
  item.expectations.sort();
  item.platforms.sort();
  // Delete comments for PASS expectations. They are likely outdated.
  if (item.expectations.length === 1 && item.expectations[0] === 'PASS') {
    delete item.comment;
  }
});

function isSubset(superset, subset) {
  let isSubset = true;

  for (const p of subset) {
    if (!superset.has(p)) {
      isSubset = false;
    }
  }

  return isSubset;
}

const toBeRemoved = new Set();
for (let i = testExpectations.length - 1; i >= 0; i--) {
  const expectation = testExpectations[i];
  const params = new Set(expectation.parameters);
  const labels = new Set(expectation.expectations);
  const platforms = new Set(expectation.platforms);

  let foundMatch = false;
  for (let j = i - 1; j >= 0; j--) {
    const candidate = testExpectations[j];
    const candidateParams = new Set(candidate.parameters);
    const candidateLabels = new Set(candidate.expectations);
    const candidatePlatforms = new Set(candidate.platforms);

    if (
      testIdMatchesExpectationPattern(
        expectation.testIdPattern,
        candidate.testIdPattern
      ) &&
      isSubset(candidateParams, params) &&
      isSubset(candidatePlatforms, platforms)
    ) {
      foundMatch = true;
      if (isSubset(candidateLabels, labels)) {
        console.log('removing', expectation, 'already covered by', candidate);
        toBeRemoved.add(expectation);
      }
      break;
    }
  }

  if (!foundMatch && isSubset(new Set(['PASS']), labels)) {
    console.log(
      'removing',
      expectation,
      'because the default expectation is to pass'
    );
    toBeRemoved.add(expectation);
  }
}

testExpectations = testExpectations.filter(item => {
  return !toBeRemoved.has(item);
});

if (process.argv.includes('--lint')) {
  const missingComments = [];
  testExpectations.forEach(item => {
    if (item.expectations.length === 1 && item.expectations[0] === 'PASS') {
      return;
    }
    if (!item.comment) {
      missingComments.push(item);
    }
  });

  if (
    JSON.stringify(committedExpectations) !== JSON.stringify(testExpectations)
  ) {
    console.error(
      `${source} is not formatted properly. Run 'npm run format:expectations'.`
    );
    process.exit(1);
  }

  if (missingComments.length > 0) {
    console.error(
      `${source}: missing comments for the following expectations:`,
      missingComments
    );
    process.exit(1);
  }
} else {
  fs.writeFileSync(
    source,
    await prettier.format(JSON.stringify(testExpectations), {
      ...prettierConfig,
      parser: 'json',
    })
  );
}