diff --git a/tools/apiview/emitters/typespec-apiview/main.tsp b/tools/apiview/emitters/typespec-apiview/main.tsp index c0a19622c6dc..1cc06db4ddac 100644 --- a/tools/apiview/emitters/typespec-apiview/main.tsp +++ b/tools/apiview/emitters/typespec-apiview/main.tsp @@ -9,286 +9,15 @@ using TypeSpec.Versioning; using Azure.Core; using Azure.Core.Traits; -@useAuth( - ApiKeyAuth | OAuth2Auth<[ - { - type: OAuth2FlowType.implicit, - authorizationUrl: "https://login.contoso.com/common/oauth2/v2.0/authorize", - scopes: ["https://widget.contoso.com/.default"], - } - ]> -) -@service({ - title: "Contoso Widget Manager", -}) -@server( - "{endpoint}/widget", - "Contoso Widget APIs", - { - /** -Supported Widget Services endpoints (protocol and hostname, for example: -https://westus.api.widget.contoso.com). - */ - endpoint: string, +#suppress "deprecated" +@TypeSpec.service( + { + title: "Test", + version: "1" } ) -@versioned(Contoso.WidgetManager.Versions) -namespace Contoso.WidgetManager; - -/** The Contoso Widget Manager service version. */ -enum Versions { - /** Version 2022-08-31 */ - @useDependency(Azure.Core.Versions.v1_0_Preview_2) - `2022-08-30`, -} - -// Models //////////////////// - -/** The color of a widget. */ -union WidgetColor { - string, - - /** Black Widget Color */ - Black: "Black", - - /** White Widget Color */ - White: "White", - - /** Red Widget Color */ - Red: "Red", - - /** Green Widget Color */ - Green: "Green", - - /** Blue Widget Color */ - Blue: "Blue", -} - -/** A widget. */ -@resource("widgets") -model Widget { - /** The widget name. */ - @key("widgetName") - @visibility("read") - name: string; - - /** The widget color. */ - color: WidgetColor; - - /** The ID of the widget's manufacturer. */ - manufacturerId: string; - - ...EtagProperty; -} - -/** The repair state of a widget. */ -@lroStatus -union WidgetRepairState { - string, - - /** Widget repairs succeeded. */ - Succeeded: "Succeeded", +namespace Azure.Test; - /** Widget repairs failed. */ - Failed: "Failed", - - /** Widget repairs were canceled. */ - Canceled: "Canceled", - - /** Widget was sent to the manufacturer. */ - SentToManufacturer: "SentToManufacturer", -} - -/** A submitted repair request for a widget. */ -model WidgetRepairRequest { - /** The state of the widget repair request. */ - requestState: WidgetRepairState; - - /** The date and time when the repair is scheduled to occur. */ - scheduledDateTime: utcDateTime; - - /** The date and time when the request was created. */ - createdDateTime: utcDateTime; - - /** The date and time when the request was updated. */ - updatedDateTime: utcDateTime; - - /** The date and time when the request was completed. */ - completedDateTime: utcDateTime; -} - -/** The parameters for a widget status request */ -model WidgetRepairStatusParams { - /** The ID of the widget being repaired. */ - @path - widgetId: string; +model ConstrainedComplex { + prop: X; } - -/** A widget's part. */ -@resource("parts") -@parentResource(Widget) -model WidgetPart { - /** The name of the part. */ - @key("widgetPartName") - @visibility("read") - name: string; - - /** The ID to use for reordering the part. */ - partId: string; - - /** The ID of the part's manufacturer. */ - manufacturerId: string; - - ...EtagProperty; -} - -/** The details of a reorder request for a WidgetPart. */ -model WidgetPartReorderRequest { - /** Identifies who signed off the reorder request. */ - signedOffBy: string; -} - -// An example of a singleton resource -/** Provides analytics about the use and maintenance of a Widget. */ -@resource("analytics") -@parentResource(Widget) -model WidgetAnalytics { - /** The identifier for the analytics object. There is only one named 'current'. */ - @key("analyticsId") - @visibility("read") - id: "current"; - - /** The number of uses of the widget. */ - useCount: int64; - - /** The number of times the widget was repaired. */ - repairCount: int64; -} - -/** A manufacturer of widgets. */ -@resource("manufacturers") -model Manufacturer { - /** The manufacturer's unique ID. */ - @key("manufacturerId") - @visibility("read") - id: string; - - /** The manufacturer's name. */ - name: string; - - /** The manufacturer's full address. */ - address: string; - - ...EtagProperty; -} - -// Operations //////////////////// - -alias ServiceTraits = SupportsRepeatableRequests & - SupportsConditionalRequests & - SupportsClientRequestId; - -alias Operations = Azure.Core.ResourceOperations; - -interface Widgets { - // Operation Status - /** Gets status of a Widget operation. */ - @sharedRoute - getWidgetOperationStatus is Operations.GetResourceOperationStatus; - /** Gets status of a Widget delete operation. */ - @sharedRoute - getWidgetDeleteOperationStatus is Operations.GetResourceOperationStatus; - - // Widget Operations - /** Creates or updates a Widget asynchronously */ - @pollingOperation(Widgets.getWidgetOperationStatus) - createOrUpdateWidget is Operations.LongRunningResourceCreateOrUpdate; - - /** Get a Widget */ - getWidget is Operations.ResourceRead; - - /** Delete a Widget asynchronously. */ - @pollingOperation(Widgets.getWidgetDeleteOperationStatus) - deleteWidget is Operations.LongRunningResourceDelete; - - /** List Widget resources */ - listWidgets is Operations.ResourceList< - Widget, - ListQueryParametersTrait - >; - - // Widget Analytics - /** Get a WidgetAnalytics */ - getAnalytics is Operations.ResourceRead; - - /** Creates or updates a WidgetAnalytics */ - updateAnalytics is Operations.ResourceCreateOrUpdate; - - // Widget Repair Operations - /** Get the status of a WidgetRepairRequest. */ - #suppress "@azure-tools/typespec-azure-core/use-standard-operations" "This is a custom operation status endpoint." - @route("/widgets/{widgetId}/repairs/{operationId}") - getRepairStatus is Foundations.GetOperationStatus; - - /** Schedule a widget for repairs. */ - @pollingOperation(Widgets.getWidgetOperationStatus) - scheduleRepairs is Operations.LongRunningResourceAction< - Widget, - WidgetRepairRequest, - WidgetRepairRequest & RequestIdResponseHeader - >; -} - -interface WidgetParts { - /** Gets status of a WidgetPart operation. */ - getWidgetPartOperationStatus is Operations.GetResourceOperationStatus; - - /** Creates a WidgetPart */ - createWidgetPart is Operations.ResourceCreateWithServiceProvidedName; - - /** Get a WidgetPart */ - getWidgetPart is Operations.ResourceRead; - - /** Delete a WidgetPart */ - deleteWidgetPart is Operations.ResourceDelete; - - /** List WidgetPart resources */ - listWidgetParts is Operations.ResourceList; - - /** Reorder all parts for the widget. */ - @pollingOperation(WidgetParts.getWidgetPartOperationStatus) - reorderParts is Operations.LongRunningResourceCollectionAction< - WidgetPart, - WidgetPartReorderRequest, - never - >; -} - -interface Manufacturers { - /** Gets status of a Manufacturer operation. */ - getManufacturerOperationStatus is Operations.GetResourceOperationStatus; - - /** Creates or replaces a Manufacturer */ - createManufacturer is Operations.ResourceCreateOrReplace; - - /** Get a Manufacturer */ - getManufacturer is Operations.ResourceRead; - - /** Delete a Manufacturer asynchronously. */ - @pollingOperation(Manufacturers.getManufacturerOperationStatus) - deleteManufacturer is Operations.LongRunningResourceDelete; - - /** List Manufacturer resources */ - listManufacturers is Operations.ResourceList; -} - -// A "global" RPC operation -/** Responds with status information about the overall service. */ -@route("service-status") -op serviceStatus is RpcOperation< - {}, - { - statusString: string; - }, - ServiceTraits ->; diff --git a/tools/apiview/emitters/typespec-apiview/src/apiview.ts b/tools/apiview/emitters/typespec-apiview/src/apiview.ts index 6d7f9016859f..83a5b1d0cda6 100644 --- a/tools/apiview/emitters/typespec-apiview/src/apiview.ts +++ b/tools/apiview/emitters/typespec-apiview/src/apiview.ts @@ -125,13 +125,69 @@ export class ApiView { } } + /** Apply workarounds to the model before output */ + private adjustLines(lines: ReviewLine[]) { + let currentContext: string | undefined = undefined; + let contextMatchFound: boolean = false; + for (const line of lines) { + // run the normal adjust line logic + this.adjustLine(line); + + const lineId = line.LineId; + if (lineId === "Azure.Test.ConstrainedComplex") { + let test = "best"; + } + const relatedTo = line.RelatedToLine; + const isContextEnd = line.IsContextEndLine; + + if (isContextEnd && relatedTo) { + throw new Error("Context end line should not have a relatedTo line."); + } + + if (currentContext) { + if (lineId === currentContext) { + contextMatchFound = true; + line.RelatedToLine = undefined; + line.IsContextEndLine = false; + } + if (relatedTo && currentContext !== relatedTo) { + if (!contextMatchFound) { + // catches the scenario where the relatedTo line is different within what should be the current context + throw new Error("Mismatched contexts. Expected ${currentContext}, got ${lineId}"); + } else { + // covers the instance where there never is an IsContextEndLine set, which happens if there's no closing brace + // on a separate line + currentContext = relatedTo; + } + } else { + line.RelatedToLine = currentContext; + } + if (isContextEnd) { + currentContext = undefined; + contextMatchFound = false; + line.RelatedToLine = undefined; + } + } else { + // if currentContext isn't set and we encounter a relatedTo line, set the current context + if (relatedTo) { + currentContext = relatedTo; + // if currentContext isn't set but there's a lineId with childrent, set the current context + } else if (lineId && line.Children.length > 0) { + currentContext = lineId; + contextMatchFound = true; + // If a context end is found without a start, then ignore the contextEnd + } else if (isContextEnd) { + line.IsContextEndLine = false; + } + } + } + } + private adjustLine(line: ReviewLine) { for (const token of line.Tokens) { this.adjustToken(token); } - for (const child of line.Children) { - this.adjustLine(child); - } + this.adjustLines(line.Children); } private adjustToken(token: ReviewToken) { @@ -146,10 +202,7 @@ export class ApiView { /** Output the APIView model to the CodeFile JSON format. */ asCodeFile(): CodeFile { - // apply workarounds to the model before output - for (const line of this.reviewLines) { - this.adjustLine(line); - } + this.adjustLines(this.reviewLines); return { Name: this.name, PackageName: this.packageName, @@ -244,7 +297,7 @@ export class ApiView { } } - private newline(isEndContext?: boolean) { + private newline() { // ensure no trailing space at the end of the line if (this.currentLine.Tokens.length > 0) { const lastToken = this.currentLine.Tokens[this.currentLine.Tokens.length - 1]; @@ -252,10 +305,7 @@ export class ApiView { const firstToken = this.currentLine.Tokens[0]; firstToken.HasPrefixSpace = false; } - - if (isEndContext) { - this.currentLine.IsContextEndLine = true; - } + if (this.currentParent) { this.currentParent.Children.push(this.currentLine); } else { @@ -324,8 +374,8 @@ export class ApiView { private lineMarker(options?: {value?: string, addCrossLanguageId?: boolean, relatedLineId?: string}) { this.currentLine.LineId = options?.value ?? this.namespaceStack.value(); - this.currentLine.RelatedToLine = options?.relatedLineId; this.currentLine.CrossLanguageId = options?.addCrossLanguageId ? (options?.value ?? this.namespaceStack.value()) : undefined; + this.currentLine.RelatedToLine = options?.relatedLineId; } private punctuation(value: string, options?: ReviewTokenOptions & {snapTo?: string, isContextEndLine?: boolean}) { @@ -389,11 +439,9 @@ export class ApiView { }); } else { this.punctuation(`"""`, options); - this.copyRelatedToFromPreviousLine(); this.newline(); for (const line of lines) { this.literal(line, options); - this.copyRelatedToFromPreviousLine(); this.newline(); } this.punctuation(`"""`, options); @@ -1100,19 +1148,11 @@ export class ApiView { } } if (!inline) { - this.copyRelatedToFromPreviousLine(); this.newline() } } } - private copyRelatedToFromPreviousLine() { - const parentCollection = this.currentParent ? this.currentParent.Children : this.reviewLines; - const lastChild = parentCollection[parentCollection.length - 1]; - if (!lastChild) return; - this.currentLine.RelatedToLine = lastChild.RelatedToLine; - } - private getFullyQualifiedIdentifier(node: MemberExpressionNode, suffix?: string): string { switch (node.base.kind) { case SyntaxKind.Identifier: diff --git a/tools/apiview/emitters/typespec-apiview/test/apiview.test.ts b/tools/apiview/emitters/typespec-apiview/test/apiview.test.ts index 459410456581..36c187a31d2a 100644 --- a/tools/apiview/emitters/typespec-apiview/test/apiview.test.ts +++ b/tools/apiview/emitters/typespec-apiview/test/apiview.test.ts @@ -2,10 +2,11 @@ import { apiViewFor, apiViewText, compare } from "./test-host.js"; import { CodeFile, ReviewLine } from "../src/schemas.js"; import { describe, it } from "vitest"; import { fail } from "assert"; +import { isDeepStrictEqual } from "util"; interface ReviewLineData { - prefixCount: number; - suffixCount: number; + relatedToCount: number; + isContextEndCount: number; } describe("apiview: tests", () => { @@ -34,55 +35,62 @@ describe("apiview: tests", () => { } /** Validates that related lines point to a valid line. */ - function validateRelatedLines(apiview: CodeFile, data: Map) { - const lineIdsFound = new Set(); + function getRelatedLineMetadata(apiview: CodeFile): Map { - function validateReviewLines(lines: ReviewLine[] | undefined) { - if (lines === undefined || lines.length === 0) return; - lines.forEach((line, index) => { - const lineId = line.LineId; + function getReviewLinesMetadata(lines: ReviewLine[] | undefined): Map | undefined { + if (lines === undefined || lines.length === 0) return undefined; + const mainMap = new Map(); + let lastKey: string | undefined = undefined; + for (const line of lines) { const related = line.RelatedToLine; - // check for a closing } IsConextEndLine - if (lineId !== undefined && lineId !== "") { - lineIdsFound.add(lineId); - const next = lines[index + 1]; - let meta = data.get(lineId); - if (meta) { - if (next?.IsContextEndLine) { - meta.suffixCount--; - } + const lineId = line.LineId + const isEndContext = line.IsContextEndLine; + if (related) { + lastKey = related; + if (!mainMap.has(related)) { + mainMap.set(related, { relatedToCount: 0, isContextEndCount: 0 }); } + mainMap.get(related)!.relatedToCount++; } - // check is this is a prefix line - if (related !== undefined) { - let meta = data.get(related); - if (meta === undefined) { - return; + if (isEndContext) { + if (lastKey === undefined) { + fail("isEndContext without a related line."); } - if (!lineIdsFound.has(related)) { - meta.prefixCount--; + if (!mainMap.has(lastKey)) { + mainMap.set(lastKey, { relatedToCount: 0, isContextEndCount: 0 }); + } + mainMap.get(lastKey)!.isContextEndCount++; + } + if (line.Children?.length > 0) { + if (lineId === undefined) { + fail("Children without a line ID."); + } + lastKey = lineId; + const childMap = getReviewLinesMetadata(line.Children); + if (childMap !== undefined && childMap.size > 0) { + for (const [key, value] of childMap) { + mainMap.set(key, value); + } } } - validateReviewLines(line.Children); - }); - } - - validateReviewLines(apiview.ReviewLines); - // verify that all counts are 0 - const keysToRemove = []; - for (const [lineId, meta] of data) { - if (meta.prefixCount == 0 && meta.suffixCount == 0) { - keysToRemove.push(lineId); } + return mainMap; } - for (const key of keysToRemove) { - data.delete(key); - } - if (data.size > 0) { - fail(`Related line mismatches found!: ${JSON.stringify(Object.fromEntries(data))}`); - } + const countMap = getReviewLinesMetadata(apiview.ReviewLines); + return countMap ?? new Map(); } + function compareCounts(lhs: Map, rhs: Map) { + // ensure the keys are the same + const lhsKeys = new Set([...lhs.keys()]); + const rhsKeys = new Set([...rhs.keys()]); + const combined = new Set([...lhsKeys, ...rhsKeys]); + if (combined.size != lhsKeys.size) { + fail(`Keys mismatch: ${JSON.stringify([...lhsKeys])} vs ${JSON.stringify([...rhsKeys])}`); + } + isDeepStrictEqual(lhs, rhs); + } + describe("models", () => { it("composition", async () => { @@ -138,13 +146,13 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Animal", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Cat", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Dog", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Pet", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Pig", { prefixCount: 0, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Animal", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Cat", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Dog", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Pet", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -223,16 +231,15 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.ConstrainedComplex", { prefixCount: 0, suffixCount: 2 }], - ["Azure.Test.ConstrainedSimple", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.ConstrainedWithDefault", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.NamedStringThing", { prefixCount: 0, suffixCount: 0 }], - ["Azure.Test.Page", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.StringPage", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.StringThing", { prefixCount: 0, suffixCount: 0 }], - ["Azure.Test.Thing", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.ConstrainedComplex", { relatedToCount: 1, isContextEndCount: 1 }], + ["Azure.Test.ConstrainedSimple", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.ConstrainedWithDefault", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Page", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.StringPage", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Thing", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -261,9 +268,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Foo", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Foo", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -287,9 +295,9 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Password", { prefixCount: 0, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], ])); }); @@ -310,9 +318,9 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.ternary", { prefixCount: 0, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], ])); }); @@ -335,9 +343,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Unreal", { prefixCount: 1, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Unreal", { relatedToCount: 1, isContextEndCount: 0 }], ])); }); }); @@ -368,10 +377,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Animal", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Creature", { prefixCount: 0, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Animal", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -400,10 +409,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Animal", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Template", { prefixCount: 0, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Animal", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); }); @@ -434,10 +443,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Animal", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.@@doc.Animal", { prefixCount: 0, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Animal", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); }); @@ -464,9 +473,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.SomeEnum", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.SomeEnum", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -491,9 +501,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.SomeStringEnum", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.SomeStringEnum", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -518,9 +529,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.SomeIntEnum", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.SomeIntEnum", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -548,10 +560,11 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.SomeEnum", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.SomeSpreadEnum", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.SomeEnum", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.SomeSpreadEnum", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); }); @@ -607,12 +620,13 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Cat", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Dog", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.MyUnion", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Snake", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Cat", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Dog", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.MyUnion", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Snake", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -662,12 +676,13 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Cat", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Dog", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Animals", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Snake", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Cat", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Dog", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Animals", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Snake", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); }); @@ -701,11 +716,11 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.GetFoo", { prefixCount: 0, suffixCount: 0 }], - ["Azure.Test.NamedGetFoo", { prefixCount: 0, suffixCount: 0 }], - ["Azure.Test.ResourceRead", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.NamedGetFoo", { relatedToCount: 1, isContextEndCount: 0 }], + ["Azure.Test.ResourceRead", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -758,10 +773,11 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Foo", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.Temp", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Foo", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Temp", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -857,16 +873,18 @@ describe("apiview: tests", () => { b: string; } }`; + // Related line mismatches found!: {"Azure.Test.NamedGetFoo":{"relatedToCount":1,"isContextEndCount":-1}} const apiview = await apiViewFor(input, {}); const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.GetFoo", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.NamedGetFoo", { prefixCount: 1, suffixCount: 0 }], - ["Azure.Test.ResourceRead", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.FooParams", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.GetFoo", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.NamedGetFoo", { relatedToCount: 1, isContextEndCount: 1 }], + ["Azure.Test.ResourceRead", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.FooParams", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -947,12 +965,13 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.GetFoo", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.NamedGetFoo", { prefixCount: 1, suffixCount: 0 }], - ["Azure.Test.ResourceRead", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.FooParams", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.GetFoo", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.NamedGetFoo", { relatedToCount: 1, isContextEndCount: 1 }], + ["Azure.Test.ResourceRead", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.FooParams", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); @@ -994,12 +1013,12 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.GetFoo", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.NamedGetFoo", { prefixCount: 1, suffixCount: 0 }], - ["Azure.Test.ResourceRead", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.FooParams", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.GetFoo", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.NamedGetFoo", { relatedToCount: 1, isContextEndCount: 1 }], + ["Azure.Test.ResourceRead", { relatedToCount: 0, isContextEndCount: 1 }] ])); }); @@ -1034,9 +1053,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.SomeOp", { prefixCount: 0, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.SomeOp", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); }); @@ -1078,11 +1098,12 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Foo", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.get", { prefixCount: 2, suffixCount: 1 }], - ["Azure.Test.list", { prefixCount: 2, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Foo", { relatedToCount: 0, isContextEndCount: 1 }], + ["Azure.Test.Foo.get", { relatedToCount: 2, isContextEndCount: 1 }], + ["Azure.Test.Foo.list", { relatedToCount: 2, isContextEndCount: 0 }], ])); }); }); @@ -1115,9 +1136,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Bar", { prefixCount: 5, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Bar", { relatedToCount: 5, isContextEndCount: 0 }], ])); }); @@ -1140,9 +1162,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Foo", { prefixCount: 1, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Foo", { relatedToCount: 1, isContextEndCount: 0 }], ])); }); }); @@ -1191,11 +1214,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Person", { prefixCount: 0, suffixCount: 1 }], - ["Azure.Test.myconst", { prefixCount: 0, suffixCount: 0 }], - ["Azure.Test.Template", { prefixCount: 0, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Person", { relatedToCount: 0, isContextEndCount: 1 }], ])); }); }); @@ -1226,9 +1248,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.Foo", { prefixCount: 2, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.Foo", { relatedToCount: 2, isContextEndCount: 1 }], ])); }); @@ -1262,9 +1285,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.SubNamespace", { prefixCount: 2, suffixCount: 1 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.SubNamespace", { relatedToCount: 2, isContextEndCount: 1 }], ])); }); @@ -1287,9 +1311,10 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.someOp", { prefixCount: 1, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], + ["Azure.Test.someOp", { relatedToCount: 1, isContextEndCount: 0 }], ])); }); }); @@ -1316,11 +1341,9 @@ describe("apiview: tests", () => { const actual = apiViewText(apiview); compare(expect, actual, 10); validateLineIds(apiview); - validateRelatedLines(apiview, new Map([ - ["Azure.Test", { prefixCount: 3, suffixCount: 1 }], - ["Azure.Test.a", { prefixCount: 0, suffixCount: 0 }], - ["Azure.Test.b", { prefixCount: 0, suffixCount: 0 }], - ["Azure.Test.c", { prefixCount: 0, suffixCount: 0 }], + const counts = getRelatedLineMetadata(apiview); + compareCounts(counts, new Map([ + ["Azure.Test", { relatedToCount: 3, isContextEndCount: 1 }], ])); }); }); diff --git a/tools/apiview/emitters/typespec-apiview/vitest.config.ts b/tools/apiview/emitters/typespec-apiview/vitest.config.ts index 485d0c73d1a5..d78b8d75d04d 100644 --- a/tools/apiview/emitters/typespec-apiview/vitest.config.ts +++ b/tools/apiview/emitters/typespec-apiview/vitest.config.ts @@ -3,15 +3,17 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { environment: 'node', - isolate: false, coverage: { - reporter: ['cobertura', 'json', 'text'], + provider: 'v8', + reporter: ['text', 'json', 'html'], include: ['src/**/*.ts'], }, outputFile: { junit: './test-results.xml', }, + reporters: 'default', exclude: ['node_modules', 'dist/test', 'dist'], + silent: false, }, esbuild: { sourcemap: true,