Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions scripts/__tests__/size-report-post-comment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import assert from 'node:assert/strict';
import { execFile } from 'node:child_process';
import { mkdtemp, readFile, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { promisify } from 'node:util';
import { test } from 'vitest';

const execFileAsync = promisify(execFile);
const ROOT = join(import.meta.dirname, '..', '..');
const SCRIPT = join(ROOT, 'scripts', 'size-report.mjs');
const MARKER = '<!-- agent-device-size-report -->';

// Each entry answers one fetch call, in order. `net` rejects the fetch (a
// dropped connection); a number is the HTTP status; `body` is the response
// text (defaults to `[]` for 200 so a list call sees no comments).
type StubResponse = { status: number | 'net'; body?: string };

const FETCH_STUB = `
import fs from 'node:fs';
const script = JSON.parse(process.env.SIZE_REPORT_FETCH_SCRIPT);
let index = 0;
globalThis.fetch = async (url, init) => {
const step = script[Math.min(index, script.length - 1)];
index += 1;
fs.appendFileSync(process.env.SIZE_REPORT_FETCH_LOG, \`\${init?.method ?? 'GET'} \${url}\\n\`);
if (step.status === 'net') throw new Error('ECONNRESET');
const body = step.body ?? (step.status === 200 ? '[]' : \`{"message":"status \${step.status}"}\`);
return new Response(body, { status: step.status });
};
`;

async function runPostComment(script: StubResponse[]) {
const dir = await mkdtemp(join(tmpdir(), 'size-report-post-comment-'));
const stubPath = join(dir, 'fetch-stub.mjs');
const reportPath = join(dir, 'report.md');
const logPath = join(dir, 'fetch.log');
const summaryPath = join(dir, 'summary.md');
await Promise.all([
writeFile(stubPath, FETCH_STUB),
writeFile(reportPath, `${MARKER}\n# report\n`),
writeFile(logPath, ''),
writeFile(summaryPath, ''),
]);
const env = {
...process.env,
GITHUB_TOKEN: 'token',
GITHUB_REPOSITORY: 'owner/repo',
GITHUB_PR_NUMBER: '1',
GITHUB_STEP_SUMMARY: summaryPath,
SIZE_REPORT_RETRY_BASE_MS: '1',
SIZE_REPORT_FETCH_SCRIPT: JSON.stringify(script),
SIZE_REPORT_FETCH_LOG: logPath,
};
let exitCode = 0;
let stdout = '';
let stderr = '';
try {
({ stdout, stderr } = await execFileAsync(
process.execPath,
['--import', stubPath, SCRIPT, '--post-comment', reportPath],
{ env },
));
} catch (error) {
const failure = error as { code?: number; stdout?: string; stderr?: string };
exitCode = failure.code ?? 1;
stdout = failure.stdout ?? '';
stderr = failure.stderr ?? '';
}
const calls = (await readFile(logPath, 'utf8')).trim().split('\n').filter(Boolean);
const summary = await readFile(summaryPath, 'utf8');
return { exitCode, stdout, stderr, calls, summary };
}

const COMMENTS_URL = 'https://api.github.com/repos/owner/repo/issues/1/comments';
const EXISTING_COMMENT_URL = `${COMMENTS_URL}/42`;
const existingMarkerComment = JSON.stringify([
{ url: EXISTING_COMMENT_URL, body: `${MARKER}\nold` },
]);

test('a create whose response was lost is reconciled into an update, not a duplicate', async () => {
const result = await runPostComment([
{ status: 200 }, // list: nothing yet
{ status: 'net' }, // create: connection dropped, but it landed server-side
{ status: 200, body: existingMarkerComment }, // re-list: marker comment now exists
{ status: 200 }, // update
]);
assert.equal(result.exitCode, 0, result.stderr);
assert.deepEqual(result.calls, [
`GET ${COMMENTS_URL}?per_page=100`,
`POST ${COMMENTS_URL}`,
`GET ${COMMENTS_URL}?per_page=100`,
`PATCH ${EXISTING_COMMENT_URL}`,
]);
});

test('transient failures that outlast the retries warn and exit 0', async () => {
const result = await runPostComment([{ status: 503 }]);
assert.equal(result.exitCode, 0, result.stderr);
assert.match(
result.stdout,
/^::warning::Skipping PR size comment after transient GitHub failure: .*503/m,
);
assert.match(result.summary, /Skipping PR size comment/);
assert.equal(result.calls.length, 4, 'one list call per attempt');
});

test('a non-transient 4xx is fatal and is not retried', async () => {
const result = await runPostComment([{ status: 200 }, { status: 401 }]);
assert.notEqual(result.exitCode, 0);
assert.match(result.stderr, /Failed to create PR comment: 401/);
assert.doesNotMatch(result.stdout, /::warning::/);
assert.equal(result.calls.length, 2, 'no retry after a fatal status');
});
2 changes: 1 addition & 1 deletion scripts/maestro-conformance/corpus/authored/doubletap.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
appId: com.example.app
---
- doubleTapOn: "Button"
- doubleTapOn: 'Button'
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ appId: com.example.app
---
- extendedWaitUntil:
visible:
id: "Item"
id: 'Item'
timeout: 1000
- extendedWaitUntil:
notVisible:
id: "Another"
id: 'Another'
timeout: 1000
2 changes: 1 addition & 1 deletion scripts/maestro-conformance/corpus/authored/repeat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ appId: com.example.app
- repeat:
times: 3
commands:
- tapOn: "Button"
- tapOn: 'Button'
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ appId: com.example.include
---
- launchApp
- tapOn:
id: "included-button"
id: 'included-button'
4 changes: 2 additions & 2 deletions scripts/maestro-conformance/corpus/authored/runflow-main.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
appId: com.example.app
---
- tapOn: "Before"
- tapOn: 'Before'
- runFlow: runflow-child.yaml
- tapOn: "After"
- tapOn: 'After'
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ appId: com.example.app
---
- scrollUntilVisible:
element:
text: "Test"
text: 'Test'
direction: DOWN
timeout: 10000
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
appId: com.example.app
---
- swipe:
start: "50.5%, 50%"
end: "10%, 50%"
start: '50.5%, 50%'
end: '10%, 50%'
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ appId: com.example.app
- retry:
maxRetries: 99
commands:
- tapOn: "Retry"
- tapOn: 'Retry'
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
# same name (no reflectable upstream constant exists).
appId: com.example.app
---
- tapOn: "Submit"
- tapOn: 'Submit'
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ appId: com.example.app
---
- swipe:
from:
id: "row"
id: 'row'
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# The command document must be a sequence.
appId: com.example.app
---
tapOn: "Button"
tapOn: 'Button'
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
appId: com.example.app
---
- tapOn:
- text: "Button"
- text: 'Button'
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Upstream rejects an unknown command name (typo of tapOn).
appId: com.example.app
---
- tapOnn: "Button"
- tapOnn: 'Button'
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
appId: com.example.app
---
- tapOn:
text: "Button"
text: 'Button'
bogusField: true
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- assertVisible:
id: "element_id"
id: 'element_id'
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- assertVisible:
text: "Element Text"
text: 'Element Text'
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- tapOn:
text: ".*button.*"
text: '.*button.*'
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
appId: com.example.app
---
- tapOn:
text: "Optional Element"
text: 'Optional Element'
optional: true
- assertVisible:
text: "Non Optional"
optional: false
text: 'Non Optional'
optional: false
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
appId: com.example.app
---
- scroll
- scroll
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
appId: com.example.app
---
- back
- back
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- inputText: "Hello World"
- inputText: user@example.com
- inputText: 'Hello World'
- inputText: user@example.com
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
appId: com.example.app
---
- launchApp
- launchApp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- tapOn:
point: 100,200
point: 100,200
2 changes: 1 addition & 1 deletion scripts/maestro-conformance/corpus/upstream/017_swipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ appId: com.example.app
- swipe:
start: 100,500
end: 100,200
duration: 3000
duration: 3000
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- launchApp:
clearState: true
clearState: true
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- assertNotVisible:
id: "element_id"
id: 'element_id'
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
appId: com.example.app
---
- openLink: https://example.com
- openLink: https://example.com
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- longPressOn:
text: ".*button.*"
text: '.*button.*'
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ appId: com.example.app
- tapOn:
text: Item.*
index: ${0 + 1}
retryTapIfNoChange: false
retryTapIfNoChange: false
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,3 @@ appId: com.example.app
- pressKey: TV Input HDMI 1
- pressKey: TV Input HDMI 2
- pressKey: TV Input HDMI 3

Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
appId: com.example.app
---
- hideKeyboard
- hideKeyboard
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
appId: com.example.app
env:
TIMEOUT: 1000
TIMEOUT: 1000
---
- extendedWaitUntil:
visible: Item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ appId: com.other.app
times: 3
commands:
- tapOn: Button
- assertVisible: "3"
- assertVisible: '3'
- evalScript: ${output.list = [1, 2, 3]}
- repeat:
times: ${output.list.length}
commands:
- tapOn: Button
- assertVisible: "6"
- assertVisible: '6'
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ appId: com.example.app
---
- swipe:
direction: RIGHT
duration: 500
duration: 500
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- launchApp:
stopApp: false
stopApp: false
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
appId: com.example.app
---
- copyTextFrom:
id: "myId"
id: 'myId'
- pasteText
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
appId: com.example.app
---
- assertTrue: ${1+1}
- assertTrue: ${1+1}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: com.example.app
---
- waitForAnimationToEnd:
timeout: 500
timeout: 500
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ appId: com.example.app
- swipe:
direction: RIGHT
from:
text: "swiping element"
text: 'swiping element'
Loading
Loading