OXYGENHUB
◆ main oxygenhub-vscode / src / tests.ts
3.2 KiB · last changed OxygenHub 0.13.0: Drops in the editor with Share as a Drop (closes #7); Flow approvals, expedite, reports, timings and test results (closes #8); ready/draft, landable, trust reports, asset downloads and new repositories (closes #9) 6 days ago source blame history raw
1
// Flow's test reports in VS Code's Testing view (spec §9.2): the newest
2
// report on each repository's current bookmark, its suites and cases with
3
// their results, messages and output. Nothing runs here: running a test
4
// loads the hub's latest results.
5
6
import * as vscode from "vscode";
7
import { HubAuth } from "./auth";
8
import { caseId, caseOutcome, casesBySuite, duration } from "./flowview";
9
import type { HubRepo } from "./proposals";
10
11
export class HubTests implements vscode.Disposable {
12
  private readonly controller: vscode.TestController;
13
14
  constructor(
15
    private readonly auth: HubAuth,
16
    private readonly repos: () => Promise<HubRepo[]>,
17
  ) {
18
    this.controller = vscode.tests.createTestController("oxygenhub", "OxygenFlow test reports");
19
    this.controller.refreshHandler = () => this.load();
20
    this.controller.createRunProfile("Load the hub's latest results", vscode.TestRunProfileKind.Run, () => this.load(), true);
21
  }
22
23
  dispose(): void {
24
    this.controller.dispose();
25
  }
26
27
  /** Fetch each repository's newest test report and show its results. */
28
  async load(): Promise<void> {
29
    const repos = await this.repos().catch(() => [] as HubRepo[]);
30
    const roots: vscode.TestItem[] = [];
31
    const results: [vscode.TestItem, string, string, string, number][] = [];
32
    for (const repo of repos) {
33
      const client = this.auth.clientFor(repo.base);
34
      const bookmark = repo.handle.currentBookmark ?? undefined;
35
      const [newest] = await client.testReports(repo.owner, repo.name, bookmark, 1).catch(() => []);
36
      if (!newest) {
37
        continue;
38
      }
39
      const detail = await client.testReport(repo.owner, repo.name, newest.id).catch(() => undefined);
40
      if (!detail) {
41
        continue;
42
      }
43
      const key = `${repo.base}/${repo.owner}/${repo.name}`;
44
      const r = detail.report;
45
      const root = this.controller.createTestItem(key, `${repo.owner}/${repo.name}`);
46
      root.description = `${r.passed}/${r.tests} passed${r.failed ? ` · ${r.failed} failed` : ""} · run #${r.run} ${r.job} on ${r.bookmark}`;
47
      for (const [suite, cases] of casesBySuite(detail.cases)) {
48
        const s = this.controller.createTestItem(`${key}::${suite}`, suite);
49
        for (const c of cases) {
50
          const item = this.controller.createTestItem(caseId(key, c), c.name);
51
          item.description = c.classname && c.classname !== suite ? c.classname : undefined;
52
          s.children.add(item);
53
          results.push([item, c.status, c.message, c.output, c.time_ms]);
54
        }
55
        root.children.add(s);
56
      }
57
      roots.push(root);
58
    }
59
    this.controller.items.replace(roots);
60
    const run = this.controller.createTestRun(new vscode.TestRunRequest(), "OxygenFlow", false);
61
    for (const [item, status, message, output, ms] of results) {
62
      const outcome = caseOutcome(status);
63
      if (outcome === "passed") {
64
        run.passed(item, ms);
65
      } else if (outcome === "skipped") {
66
        run.skipped(item);
67
      } else {
68
        const m = new vscode.TestMessage(message || `${status} after ${duration(ms)}`);
69
        if (output) {
70
          m.actualOutput = output;
71
        }
72
        (outcome === "failed" ? run.failed : run.errored).call(run, item, m, ms);
73
      }
74
    }
75
    run.end();
76
  }
77
}