Skip to content
This repository was archived by the owner on Jan 12, 2026. It is now read-only.
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
1 change: 1 addition & 0 deletions workspaces/cli-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class CliServer {
'/api/testing',
createProxyMiddleware({
changeOrigin: true,
followRedirects: true,
target: this.config.cloudApiBaseUrl,
pathRewrite(input, req) {
return input.substring(req.baseUrl.length);
Expand Down
10 changes: 7 additions & 3 deletions workspaces/ui/src/components/testing/ReportSummary.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export default function ReportSummary(props) {
<SummaryStats
totalInteractions={totalInteractions}
totalDiffs={totalDiffs}
totalUnmatchedPaths={totalUnmatchedPaths}
totalUndocumentedEndpoints={undocumentedEndpoints.length}
/>
<h4 className={classes.buildName}>
from capturing interactions for build <code>{summary.buildId}</code>{' '}
Expand Down Expand Up @@ -248,14 +248,18 @@ export default function ReportSummary(props) {
}
ReportSummary.displayName = 'Testing/ReportSummary';

function SummaryStats({ totalInteractions, totalDiffs, totalUnmatchedPaths }) {
function SummaryStats({
totalInteractions,
totalDiffs,
totalUndocumentedEndpoints,
}) {
const classes = useStyles();

return (
<Typography variant="h6" color="primary" style={{ fontWeight: 200 }}>
Optic observed <Stat value={totalInteractions} label="interaction" />
, yielding in <Stat value={totalDiffs} label="diff" /> and{' '}
<Stat value={totalUnmatchedPaths} label="undocumented endpoint" />.
<Stat value={totalUndocumentedEndpoints} label="undocumented endpoint" />.
</Typography>
);
}
Expand Down
96 changes: 94 additions & 2 deletions workspaces/ui/src/services/testing/TestingService.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { ITestingService, Result, Capture, ok } from '.';
import {
ITestingService,
Capture,
CoverageReport,
Result,
ok,
err,
NotFoundError,
RfcEventStream,
UndocumentedEndpoint,
} from '.';
import UrlJoin from 'url-join';
import { StableHasher } from '../../utilities/CoverageUtilities';
import { opticEngine } from '@useoptic/domain';

// TODO: implement ITestingService
export class TestingService {
export class TestingService implements ITestingService {
private authToken: string;
private refreshing?: Promise<unknown>;

Expand All @@ -29,6 +41,7 @@ export class TestingService {
headers.set('Authorization', `Bearer ${this.authToken}`);

const response = await fetch(url, {
...options,
headers,
});

Expand Down Expand Up @@ -74,4 +87,83 @@ export class TestingService {

return ok(payload.captures);
}

async loadCapture(captureId): Promise<Result<Capture, NotFoundError>> {
const response = await this.callApi(`/captures/${captureId}`);

if (!response.ok) {
if (response.status === 404) {
return err(new NotFoundError());
} else {
throw new Error('Capture could not be fetched');
}
}

const payload = await response.json();

return ok(payload);
}

async loadReport(captureId): Promise<Result<CoverageReport, NotFoundError>> {
const response = await this.callApi(
`/captures/${captureId}/reports/coverage`
);

if (!response.ok) {
if (response.status === 404) {
return err(new NotFoundError());
} else {
throw new Error('CoverageReport for capture could not be fetched');
}
}

const payload = await response.json();
const deserialized = opticEngine.CoverageReportJsonDeserializer.fromJs(
payload
);
const converter = new opticEngine.com.useoptic.CoverageReportConverter(
StableHasher
);
const serializedReport = converter.toJs(deserialized);
return ok(serializedReport);
}

async loadUndocumentedEndpoints(
captureId
): Promise<Result<UndocumentedEndpoint[], NotFoundError>> {
const response = await this.callApi(
`/captures/${captureId}/reports/undocumented-urls`
);

if (!response.ok) {
if (response.status === 404) {
return err(new NotFoundError());
} else {
throw new Error(
'Undocumented endpoints for capture could not be fetched'
);
}
}

const payload = await response.json();
return ok(payload);
}

async loadSpecEvents(
captureId
): Promise<Result<RfcEventStream, NotFoundError>> {
const response = await this.callApi(`/captures/${captureId}/spec`);

if (!response.ok) {
if (response.status === 404) {
return err(new NotFoundError());
} else {
throw new Error('Spec for capture could not be fetched');
}
}

const payload = await response.json();

return ok(payload);
}
}
2 changes: 1 addition & 1 deletion workspaces/ui/src/services/testing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface ITestingService {
captureId: CaptureId
): Promise<Result<CoverageReport, NotFoundError>>;

loadEndpointDiffs(
loadEndpointDiffs?(
captureId: CaptureId,
pathId: PathId,
httpMethod: HttpMethod
Expand Down