Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework audit interviewers for CLI #699

Merged
merged 16 commits into from
Feb 24, 2021
Merged
Show file tree
Hide file tree
Changes from 15 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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Items that are related, such as breaking changes, new features, or changes to ex

- [@siteimprove/alfa-cache](packages/alfa-cache): `Cache<K, V>` now requires that `K` be an object type.

- [@siteimprove/alfa-act](packages/alfa-act): The `Oracle<Q>` type has now become `Oracle<I, T, Q>`. As such, the input type `I` and test target type `T` must now be declared up front. Additionally, the `Question<Q, A, S, T>` type has now become `Question<Q, S, A, T>` to ensure alignment with the remaining types of the package. ([#699](../../pull/699))

- [@siteimprove/alfa-cli](packages/alfa-cli): The `--interactive` flag of the `alfa audit` command has been removed. A new `--interviewer` flag has instead been made available which allows callers to point to an `Interviewer` implementation for answering questions during an audit. ([#255](../../issues/255), [#699](../../pull/699))

### Added

- [@siteimprove/alfa-predicate](packages/alfa-predicate): `Predicate.tee()` is now available.
Expand Down Expand Up @@ -63,6 +67,8 @@ Items that are related, such as breaking changes, new features, or changes to ex

- [@siteimprove/alfa-sarif](packages/alfa-sarif): A package has been added with types for working with SARIF serialisable structures. ([#694](../../pull/694))

- [@siteimprove/alfa-interviewer](packages/alfa-interviewer): A new package has been added with types for modelling ACT rule interviewers and functionality for loading these from external and local modules. ([#699](../../pull/699))

### Changed

- [@siteimprove/alfa-rules](packages/alfa-rules): SIA-R65 now automatically accepts difference in `background-color` or `color`, and presence/absence of a `box-shadow` as a valid focus indicator. ([#658](../../issues/658), [#713](../../pull/713), [#714](../../pull/714), [#715](../../pull/715))
Expand Down
10 changes: 7 additions & 3 deletions packages/alfa-act/src/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ export class Audit<I, T = unknown, Q = never> {
public static of<I, T = unknown, Q = never>(
input: I,
rules: Iterable<Rule<I, T, Q>>,
oracle: Oracle<Q> = () => Future.now(None)
oracle: Oracle<I, T, Q> = () => Future.now(None)
): Audit<I, T, Q> {
return new Audit(input, List.from(rules), oracle);
}

private readonly _input: I;
private readonly _rules: List<Rule<I, T, Q>>;
private readonly _oracle: Oracle<Q>;
private readonly _oracle: Oracle<I, T, Q>;

private constructor(input: I, rules: List<Rule<I, T, Q>>, oracle: Oracle<Q>) {
private constructor(
input: I,
rules: List<Rule<I, T, Q>>,
oracle: Oracle<I, T, Q>
) {
this._input = input;
this._rules = rules;
this._oracle = oracle;
Expand Down
4 changes: 2 additions & 2 deletions packages/alfa-act/src/interview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export type Interview<Q, S, T, D extends number = 3> =
| {
[K in keyof Q]: Question<
K,
Q[K],
S,
Q[K],
D extends -1 ? T : Interview<Q, S, T, Depths[D]>
>;
}[keyof Q];
Expand All @@ -30,7 +30,7 @@ export namespace Interview {
export function conduct<I, T, Q, A>(
interview: Interview<Q, T, A>,
rule: Rule<I, T, Q>,
oracle: Oracle<Q>
oracle: Oracle<I, T, Q>
): Future<Option<A>> {
if (interview instanceof Question) {
return oracle(rule, interview).flatMap((answer) =>
Expand Down
4 changes: 2 additions & 2 deletions packages/alfa-act/src/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Option } from "@siteimprove/alfa-option";
import { Question } from "./question";
import { Rule } from "./rule";

export type Oracle<Q> = <I, T, A>(
export type Oracle<I, T, Q> = <A>(
rule: Rule<I, T, Q>,
question: { [K in keyof Q]: Question<K, Q[K], T, A> }[keyof Q]
question: { [K in keyof Q]: Question<K, T, Q[K], A> }[keyof Q]
) => Future<Option<A>>;
10 changes: 5 additions & 5 deletions packages/alfa-act/src/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { Monad } from "@siteimprove/alfa-monad";

import * as json from "@siteimprove/alfa-json";

export class Question<Q, A, S, T = A>
export class Question<Q, S, A, T = A>
implements Functor<T>, Monad<T>, Serializable<Question.JSON<Q, S>> {
public static of<Q, A, S>(
uri: string,
type: Q,
subject: S,
message: string
): Question<Q, A, S> {
): Question<Q, S, A> {
return new Question(uri, type, subject, message, (answer) => answer);
}

Expand Down Expand Up @@ -52,7 +52,7 @@ export class Question<Q, A, S, T = A>
return this._message;
}

public map<U>(mapper: Mapper<T, U>): Question<Q, A, S, U> {
public map<U>(mapper: Mapper<T, U>): Question<Q, S, A, U> {
return new Question(
this._uri,
this._type,
Expand All @@ -63,8 +63,8 @@ export class Question<Q, A, S, T = A>
}

public flatMap<U>(
mapper: Mapper<T, Question<Q, A, S, U>>
): Question<Q, A, S, U> {
mapper: Mapper<T, Question<Q, S, A, U>>
): Question<Q, S, A, U> {
return new Question(
this._uri,
this._type,
Expand Down
6 changes: 3 additions & 3 deletions packages/alfa-act/src/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export abstract class Rule<I = unknown, T = unknown, Q = never>

public evaluate(
input: Readonly<I>,
oracle: Oracle<Q> = () => Future.now(None),
oracle: Oracle<I, T, Q> = () => Future.now(None),
outcomes: Cache = Cache.empty()
): Future<Iterable<Outcome<I, T, Q>>> {
return this._evaluate(input, oracle, outcomes);
Expand Down Expand Up @@ -150,7 +150,7 @@ export namespace Rule {
* rule evaluation procedures.
*/
export interface Evaluate<I, T, Q> {
(input: Readonly<I>, oracle: Oracle<Q>, outcomes: Cache): Future<
(input: Readonly<I>, oracle: Oracle<I, T, Q>, outcomes: Cache): Future<
Iterable<Outcome<I, T, Q>>
>;
}
Expand Down Expand Up @@ -370,7 +370,7 @@ function resolve<I, T, Q>(
[key: string]: Interview<Q, T, Option.Maybe<Result<Diagnostic>>>;
}>,
rule: Rule<I, T, Q>,
oracle: Oracle<Q>
oracle: Oracle<I, T, Q>
): Future<Outcome.Applicable<I, T, Q>> {
return Future.traverse(expectations, ([id, interview]) =>
Interview.conduct(interview, rule, oracle).map(
Expand Down
12 changes: 7 additions & 5 deletions packages/alfa-cli/bin/alfa/command/audit/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ export const Flags = {
.alias("o")
.optional(),

interactive: Flag.boolean(
"interactive",
"Whether or not to run an interactive audit."
interviewer: Flag.string(
"interviewer",
`The interviewer to use for answering questions during the audit. If not
provided, questions will be left unanswered.`
)
.type("name or package")
.alias("i")
.default(false),
.optional(),

format: Flag.string("format", "The reporting format to use.")
.type("format or package")
.type("name or package")
.alias("f")
.default("earl"),

Expand Down
30 changes: 20 additions & 10 deletions packages/alfa-cli/bin/alfa/command/audit/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import * as fs from "fs";
import { Audit, Outcome } from "@siteimprove/alfa-act";
import { Command } from "@siteimprove/alfa-command";
import { Formatter } from "@siteimprove/alfa-formatter";
import { Interviewer } from "@siteimprove/alfa-interviewer";
import { Iterable } from "@siteimprove/alfa-iterable";
import { None } from "@siteimprove/alfa-option";
import { Ok } from "@siteimprove/alfa-result";
import { Option, None } from "@siteimprove/alfa-option";
import { Result, Err } from "@siteimprove/alfa-result";
import { Page } from "@siteimprove/alfa-web";

import rules from "@siteimprove/alfa-rules";

import { Oracle } from "../../oracle";
import { Profiler } from "../../profiler";

import type { Arguments } from "./arguments";
Expand All @@ -30,6 +30,16 @@ export const run: Command.Runner<typeof Flags, typeof Arguments> = async ({
return formatter;
}

const interviewer = Option.from(
await flags.interviewer
.map((interviewer) => Interviewer.load<any, any, any>(interviewer))
.getOr(undefined)
);

if (interviewer.some((interviewer) => interviewer.isErr())) {
return interviewer.get() as Err<string>;
}

let json: string;

if (target.isNone()) {
Expand All @@ -54,11 +64,11 @@ export const run: Command.Runner<typeof Flags, typeof Arguments> = async ({

const page = Page.from(JSON.parse(json));

const audit = Audit.of(
page,
rules,
flags.interactive ? Oracle(page) : undefined
);
const oracle = interviewer
.map((interviewer) => interviewer.get()(page, rules))
.getOr(undefined);

const audit = Audit.of(page, rules, oracle);

for (const _ of flags.cpuProfile) {
await Profiler.CPU.start();
Expand Down Expand Up @@ -101,9 +111,9 @@ export const run: Command.Runner<typeof Flags, typeof Arguments> = async ({
const output = formatter.get()(page, rules, outcomes);

if (flags.output.isNone()) {
return Ok.of(output);
return Result.of(output);
} else {
fs.writeFileSync(flags.output.get(), output + "\n");
return Ok.of("");
return Result.of("");
}
};
101 changes: 0 additions & 101 deletions packages/alfa-cli/bin/alfa/oracle.ts

This file was deleted.

1 change: 1 addition & 0 deletions packages/alfa-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@siteimprove/alfa-formatter-json": "workspace:^0.11.0",
"@siteimprove/alfa-future": "workspace:^0.11.0",
"@siteimprove/alfa-http": "workspace:^0.11.0",
"@siteimprove/alfa-interviewer": "workspace:^0.11.0",
"@siteimprove/alfa-iterable": "workspace:^0.11.0",
"@siteimprove/alfa-option": "workspace:^0.11.0",
"@siteimprove/alfa-result": "workspace:^0.11.0",
Expand Down
4 changes: 3 additions & 1 deletion packages/alfa-cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"bin/alfa/command/scrape/arguments.ts",
"bin/alfa/command/scrape/flags.ts",
"bin/alfa/command/scrape/run.ts",
"bin/alfa/oracle.ts",
"bin/alfa/profiler.ts"
],
"references": [
Expand Down Expand Up @@ -55,6 +54,9 @@
{
"path": "../alfa-http"
},
{
"path": "../alfa-interviewer"
},
{
"path": "../alfa-iterable"
},
Expand Down
32 changes: 32 additions & 0 deletions packages/alfa-interviewer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"$schema": "http://json.schemastore.org/package",
"name": "@siteimprove/alfa-interviewer",
"homepage": "https://siteimprove.com",
"version": "0.11.0",
"license": "MIT",
"description": "Types for modelling ACT rule interviewers",
"repository": {
"type": "git",
"url": "https://github.com/siteimprove/alfa.git",
"directory": "packages/alfa-interviewer"
},
"bugs": "https://github.com/siteimprove/alfa/issues",
"main": "src/index.js",
"types": "src/index.d.ts",
"files": [
"src/**/*.js",
"src/**/*.d.ts"
],
"dependencies": {
"@siteimprove/alfa-act": "workspace:^0.11.0",
"@siteimprove/alfa-result": "workspace:^0.11.0",
"@types/node": "^14.14.31"
},
"devDependencies": {
"@siteimprove/alfa-test": "workspace:^0.11.0"
},
"publishConfig": {
"access": "public",
"registry": "https://npm.pkg.github.com/"
}
}
1 change: 1 addition & 0 deletions packages/alfa-interviewer/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./interviewer";
Loading