Skip to content
Open
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"genkit": "^1.19.1",
"genkitx-anthropic": "0.25.0",
"handlebars": "^4.7.8",
"lighthouse": "^12.8.2",
"limiter": "^3.0.0",
"marked": "^16.1.1",
"node-fetch": "^3.3.2",
Expand Down
812 changes: 782 additions & 30 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions report-app/src/app/pages/report-viewer/lighthouse-category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {Component, input} from '@angular/core';
import {LighthouseAudit} from '../../../../../runner/workers/serve-testing/worker-types';
import {Score} from '../../shared/score/score';

@Component({
selector: 'lighthouse-category',
imports: [Score],
template: `
@let audits = this.audits();
@let score = this.score();

<h4>
@if (score != null) {
<score size="small" [total]="score" [max]="1"/>
}
{{displayName()}}
</h4>

@if (description()) {
<p>{{description()}}</p>
}

<ul>
@for (audit of audits; track audit.id) {
<li>
@if (audit.score != null) {
<score size="tiny" [total]="audit.score" [max]="1"/>
}
{{audit.title}}{{audit.displayValue ? ': ' + audit.displayValue : ''}}

@if (audit.description) {
<span
class="material-symbols-outlined has-tooltip multiline-tooltip"
[attr.data-tooltip]="audit.description">info</span>
}
</li>
}
</ul>
`,
styles: `
:host {
display: block;
}

h4 {
display: flex;
width: 100%;
align-items: center;
gap: 0.5rem;
margin: 1rem 0 0.5rem 0;
}

ul {
display: flex;
flex-direction: column;
list-style: none;
padding: 0 0 0 4px;
gap: 0.5rem;
margin: 0;
}

li {
display: flex;
align-items: center;
gap: 0.5rem;
}
`,
})
export class LighthouseCategory {
readonly audits = input.required<LighthouseAudit[]>();
readonly displayName = input.required<string>();
readonly score = input.required<number | null>();
readonly description = input<string>();

protected toPercent(value: number) {
return Math.round(value * 100) + '%';
}
}
23 changes: 23 additions & 0 deletions report-app/src/app/pages/report-viewer/report-viewer.html
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,29 @@ <h4>Generated Code</h4>
</expansion-panel>
}

@let lighthouse = result.finalAttempt.serveTestingResult?.lighthouseResult;

@if (lighthouse) {
<expansion-panel>
<expansion-panel-header>Lighthouse</expansion-panel-header>

@for (category of lighthouse.categories; track category.id) {
<lighthouse-category
[audits]="category.audits"
[displayName]="category.displayName"
[score]="category.score"
[description]="category.description"/>
}

@if (lighthouse.uncategorized.length > 0) {
<lighthouse-category
[audits]="lighthouse.uncategorized"
displayName="Uncategorized"
[score]="null"/>
}
</expansion-panel>
}

@if (result.userJourneys && result.userJourneys.result.length > 0) {
<expansion-panel>
<expansion-panel-header>User Journeys</expansion-panel-header>
Expand Down
4 changes: 4 additions & 0 deletions report-app/src/app/pages/report-viewer/report-viewer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ expansion-panel {
margin-top: 1rem;
}

lighthouse-category + lighthouse-category {
margin-top: 2rem;
}

.summary-meta {
display: flex;
align-items: center;
Expand Down
9 changes: 3 additions & 6 deletions report-app/src/app/pages/report-viewer/report-viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@ import {
viewChild,
} from '@angular/core';
import {NgxJsonViewerModule} from 'ngx-json-viewer';
import {BuildErrorType} from '../../../../../runner/workers/builder/builder-types';
import {
BuildErrorType,
BuildResultStatus,
} from '../../../../../runner/workers/builder/builder-types';
import {
AiChatResponse,
AssessmentResult,
IndividualAssessment,
IndividualAssessmentState,
Expand All @@ -44,8 +40,8 @@ import {bucketToScoreVariable, formatScore, ScoreCssVariable} from '../../shared
import {ExpansionPanel} from '../../shared/expansion-panel/expansion-panel';
import {ExpansionPanelHeader} from '../../shared/expansion-panel/expansion-panel-header';
import {ProviderLabel} from '../../shared/provider-label';
import {firstValueFrom} from 'rxjs';
import {AiAssistant} from '../../shared/ai-assistant/ai-assistant';
import {LighthouseCategory} from './lighthouse-category';

const localReportRegex = /-l\d+$/;

Expand All @@ -63,6 +59,7 @@ const localReportRegex = /-l\d+$/;
ProviderLabel,
NgxJsonViewerModule,
AiAssistant,
LighthouseCategory,
],
templateUrl: './report-viewer.html',
styleUrls: ['./report-viewer.scss'],
Expand Down
12 changes: 12 additions & 0 deletions report-app/src/app/shared/score/score.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
align-items: center;
justify-content: center;
color: var(--text-on-status);
flex-shrink: 0;
}

:host(.excellent-score) {
Expand Down Expand Up @@ -55,3 +56,14 @@
font-size: 0.4rem;
}
}

:host(.tiny) {
width: 26px;
height: 26px;
font-size: 0.7rem;
border-radius: 6px;

.label {
font-size: 0.4rem;
}
}
2 changes: 1 addition & 1 deletion report-app/src/app/shared/score/score.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {formatScore} from '../scoring';
export class Score {
readonly total = input.required<number>();
readonly max = input.required<number>();
readonly size = input<'small' | 'medium' | 'large'>('medium');
readonly size = input<'tiny' | 'small' | 'medium' | 'large'>('medium');
readonly label = input<string>('');

protected formattedScore = computed(() => formatScore(this.total(), this.max()));
Expand Down
39 changes: 39 additions & 0 deletions report-app/src/app/shared/styles/tooltip.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@mixin tooltip-styles {
.has-tooltip {
position: relative;

&::before {
font-family: var(--font-family);
content: attr(data-tooltip);
position: absolute;
bottom: 110%;
left: 50%;
transform: translateX(-50%);
background-color: var(--tooltip-background-color);
color: var(--tooltip-text-color);
padding: 6px 12px;
border-radius: 6px;
font-size: 13px;
line-height: 1.5;
opacity: 0;
visibility: hidden;
overflow: hidden;
white-space: nowrap;
transition:
opacity 0.2s ease-in-out,
visibility 0.2s ease-in-out;
z-index: 10;
display: block;
}

&.multiline-tooltip::before {
white-space: normal;
max-width: 400px;
}

&:hover::before {
opacity: 1;
visibility: visible;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
[style.width.%]="asPercent(item.value)"
[style.background-color]="item.color"
(click)="toggleDisplayMode()"
[attr.data-tooltip]="showLegend() ? null : item.label"
>
[class.has-tooltip]="!showLegend()"
[attr.data-tooltip]="item.label">
{{ getItemDisplayValue(item) }}
</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,31 +51,6 @@
&:hover {
filter: brightness(1.1);
}

&[data-tooltip]::before {
content: attr(data-tooltip); // Use a data attribute for the text
position: absolute;
bottom: 110%; // Position it above the segment
left: 50%;
transform: translateX(-50%);
background-color: var(--tooltip-background-color);
color: var(--tooltip-text-color);
padding: 6px 12px;
border-radius: 6px;
font-size: 13px;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition:
opacity 0.2s ease-in-out,
visibility 0.2s ease-in-out;
z-index: 10;
}

&[data-tooltip]:hover::before {
opacity: 1;
visibility: visible;
}
}

.legend {
Expand Down
2 changes: 2 additions & 0 deletions report-app/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@use './app/shared/styles/callouts';
@use './app/shared/styles/cards';
@use './app/shared/styles/statuses';
@use './app/shared/styles/tooltip';

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');

Expand Down Expand Up @@ -80,6 +81,7 @@ hr {
@include callouts.callout-styles;
@include cards.card-styles;
@include statuses.status-styles;
@include tooltip.tooltip-styles;

::view-transition-old(count),
::view-transition-new(count) {
Expand Down
7 changes: 7 additions & 0 deletions runner/eval-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ interface Options {
autoraterModel?: string;
a11yRepairAttempts?: number;
logging?: 'text-only' | 'dynamic';
skipLighthouse?: boolean;
}

function builder(argv: Argv): Argv<Options> {
Expand Down Expand Up @@ -153,6 +154,11 @@ function builder(argv: Argv): Argv<Options> {
default: 0,
description: 'Number of repair attempts for discovered a11y violations',
})
.option('skip-lighthouse', {
type: 'boolean',
default: false,
description: 'Whether to skip collecting Lighthouse data',
})
.strict()
.version(false)
.help()
Expand Down Expand Up @@ -197,6 +203,7 @@ async function handler(cliArgs: Arguments<Options>): Promise<void> {
autoraterModel: cliArgs.autoraterModel,
skipAiSummary: cliArgs.skipAiSummary,
a11yRepairAttempts: cliArgs.a11yRepairAttempts,
skipLighthouse: cliArgs.skipLighthouse,
});

logReportToConsole(runInfo);
Expand Down
5 changes: 5 additions & 0 deletions runner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export {
BuildResultStatus,
type BuildResult,
} from './workers/builder/builder-types.js';
export {
type LighthouseResult,
type LighthouseCategory,
type LighthouseAudit,
} from './workers/serve-testing/worker-types.js';
export {type UserJourneysResult} from './orchestration/user-journeys.js';
export {type AutoRateResult} from './ratings/autoraters/auto-rate-shared.js';
export {DEFAULT_MODEL_NAME, REPORT_VERSION} from './configuration/constants.js';
Expand Down
3 changes: 3 additions & 0 deletions runner/orchestration/build-serve-loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export async function attemptBuild(
skipScreenshots: boolean,
skipAxeTesting: boolean,
enableAutoCsp: boolean,
skipLighthouse: boolean,
userJourneyAgentTaskInput: BrowserAgentTaskInput | undefined,
maxAxeRepairAttempts: number,
) {
Expand Down Expand Up @@ -125,6 +126,7 @@ export async function attemptBuild(
skipScreenshots,
skipAxeTesting,
enableAutoCsp,
skipLighthouse,
userJourneyAgentTaskInput,
);
}
Expand Down Expand Up @@ -194,6 +196,7 @@ export async function attemptBuild(
skipScreenshots,
skipAxeTesting,
enableAutoCsp,
skipLighthouse,
userJourneyAgentTaskInput,
);

Expand Down
4 changes: 4 additions & 0 deletions runner/orchestration/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export async function generateCodeAndAssess(options: {
logging?: 'text-only' | 'dynamic';
autoraterModel?: string;
a11yRepairAttempts?: number;
skipLighthouse?: boolean;
}): Promise<RunInfo> {
const env = await getEnvironmentByPath(options.environmentConfigPath, options.runner);
const ratingLlm = await getRunnerByName('genkit');
Expand Down Expand Up @@ -180,6 +181,7 @@ export async function generateCodeAndAssess(options: {
progress,
options.autoraterModel || DEFAULT_AUTORATER_MODEL_NAME,
options.a11yRepairAttempts ?? 0,
!!options.skipLighthouse,
),
// 10min max per app evaluation. We just want to make sure it never gets stuck.
10,
Expand Down Expand Up @@ -310,6 +312,7 @@ async function startEvaluationTask(
progress: ProgressLogger,
autoraterModel: string,
a11yRepairAttempts: number,
skipLighthouse: boolean,
): Promise<AssessmentResult[]> {
// Set up the project structure once for the root project.
const {directory, cleanup} = await setupProjectStructure(
Expand Down Expand Up @@ -433,6 +436,7 @@ async function startEvaluationTask(
skipScreenshots,
skipAxeTesting,
enableAutoCsp,
skipLighthouse,
userJourneyAgentTaskInput,
a11yRepairAttempts,
);
Expand Down
Loading
Loading