diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.spec.ts index a5859d39440..0108ee0a8e8 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.spec.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.spec.ts @@ -37,4 +37,106 @@ describe('ApiRevisionOptionsComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + describe('Tag APIRevision appropriately based on date and/or status', () => { + const apiRevisions = [ + { + id: '1', + isApproved: false, + version: "12.15.1", + apiRevisionType: 'manual', + }, + { + id: '2', + isApproved: true, + version: "12.20.0-beta.2", + changeHistory: [ + { + changeAction: 'approved', + changedOn: '2024-07-01T00:00:00Z', + } + ], + isReleased: true, + releasedOn: '2024-07-02T00:00:00Z', + apiRevisionType: 'automatic', + lastUpdatedOn: '2024-07-01T00:00:00Z', + }, + { + id: '3', + isApproved: true, + version: "12.20.0", + changeHistory: [ + { + changeAction: 'approved', + changedOn: '2024-07-04T00:00:00Z', + } + ], + isReleased: true, + releasedOn: '2024-07-05T00:00:00Z', + apiRevisionType: 'automatic', + lastUpdatedOn: '2024-07-04T00:00:00Z', + }, + { + id: '4', + isApproved: true, + version: "12.21.1", + changeHistory: [ + { + changeAction: 'approved', + changedOn: '2024-07-05T00:00:00Z', + } + ], + isReleased: false, + apiRevisionType: 'automatic', + lastUpdatedOn: '2024-07-05T00:00:00Z', + }, + { + id: '5', + isApproved: false, + version: "13.0.0", + isReleased: false, + apiRevisionType: 'automatic', + lastUpdatedOn: '2024-07-04T00:00:00Z', + }, + { + id: '6', + isApproved: true, + version: "11.0.0", + changeHistory: [ + { + changeAction: 'approved', + changedOn: '2021-07-04T00:00:00Z', + } + ], + isReleased: true, + releasedOn: '2021-07-05T00:00:00Z', + apiRevisionType: 'automatic', + lastUpdatedOn: '2021-07-04T00:00:00Z', + }, + ]; + + it('should correctly tag the latest GA APIRevision', () => { + var result = component.tagLatestGARevision(apiRevisions); + expect(result.id).toEqual('3'); + expect(result.isLatestGA).toBeTruthy(); + }); + + it('should correctly tag the latest approved APIRevision', () => { + var result = component.tagLatestApprovedRevision(apiRevisions); + expect(result.id).toEqual('4'); + expect(result.isLatestApproved).toBeTruthy(); + }); + + it('should correctly tag the latest automatic APIRevision', () => { + var result = component.tagCurrentMainRevision(apiRevisions); + expect(result.id).toEqual('4'); + expect(result.isLatestMain).toBeTruthy(); + }); + + it('should correctly tag the latest released APIRevision', () => { + var result = component.tagLatestReleasedRevision(apiRevisions); + expect(result.id).toEqual('3'); + expect(result.isLatestReleased).toBeTruthy(); + }); + }) }); diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.ts index 1810eb27cf8..c7aed8906f2 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { getQueryParams } from 'src/app/_helpers/router-helpers'; +import { AzureEngSemanticVersion } from 'src/app/_models/azureEngSemanticVersion'; import { APIRevision } from 'src/app/_models/revision'; @Component({ @@ -42,8 +43,8 @@ export class ApiRevisionOptionsComponent implements OnChanges { ngOnChanges(changes: SimpleChanges): void { if (changes['apiRevisions'] || changes['activeApiRevisionId'] || changes['diffApiRevisionId']) { if (this.apiRevisions.length > 0) { - let mappedApiRevisions = this.mapRevisionToMenu(this.apiRevisions); - this.mappedApiRevisions = this.identifyAndProcessSpecialAPIRevisions(mappedApiRevisions); + this.mappedApiRevisions = this.mapRevisionToMenu(this.apiRevisions); + this.tagSpecialRevisions(this.mappedApiRevisions); this.activeApiRevisionsMenu = this.mappedApiRevisions.filter((apiRevision: any) => apiRevision.id !== this.diffApiRevisionId); const selectedActiveAPIRevisionindex = this.activeApiRevisionsMenu.findIndex((apiRevision: APIRevision) => apiRevision.id === this.activeApiRevisionId); @@ -170,6 +171,7 @@ export class ApiRevisionOptionsComponent implements OnChanges { return { id : apiRevision.id, resolvedLabel: apiRevision.resolvedLabel, + language: apiRevision.language, label: apiRevision.label, typeClass: typeClass, apiRevisionType: apiRevision.apiRevisionType, @@ -181,6 +183,7 @@ export class ApiRevisionOptionsComponent implements OnChanges { isApproved: apiRevision.isApproved, isReleased: apiRevision.isReleased, releasedOn: apiRevision.releasedOn, + changeHistory: apiRevision.changeHistory, isLatestGA : false, isLatestApproved : false, isLatestMain : false, @@ -191,74 +194,92 @@ export class ApiRevisionOptionsComponent implements OnChanges { }); } - identifyAndProcessSpecialAPIRevisions(mappedApiRevisions: any []) { - const result = []; - const semVarRegex = /(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:(?-?)(?[a-zA-Z]+)(?:(?\.?)(?[0-9]{1,8})(?:(?\.?)(?\d{1,3}))?)?)?/; - - let latestGAApiRevision : any = null; - let latestApprovedApiRevision : any = null; - let currentMainApiRevision : any = null; - let latestReleasedApiRevision : any = null; - - while (mappedApiRevisions.length > 0) { - let apiRevision = mappedApiRevisions.shift(); + tagSpecialRevisions(mappedApiRevisions: any []) { + this.tagLatestGARevision(mappedApiRevisions); + this.tagLatestApprovedRevision(mappedApiRevisions); + this.tagCurrentMainRevision(mappedApiRevisions); + this.tagLatestReleasedRevision(mappedApiRevisions); + } - if (latestGAApiRevision === null) { - if (apiRevision.version) { - let versionParts = apiRevision.version.match(semVarRegex); + tagLatestGARevision(apiRevisions: any[]) { + const gaRevisions : any [] = []; - if (versionParts.groups?.prelabel === undefined && versionParts.groups?.prenumber === undefined && - versionParts.groups?.prenumsep === undefined && versionParts.groups?.presep === undefined) { - apiRevision.isLatestGA = true; - latestGAApiRevision = apiRevision; - continue; - } + for (let apiRevision of apiRevisions) { + if (apiRevision.isReleased && apiRevision.version) { + const semVar = new AzureEngSemanticVersion(apiRevision.version, apiRevision.language); + if (semVar.versionType == "GA") { + apiRevision.semanticVersion = semVar; + gaRevisions.push(apiRevision); } } + } - if (latestApprovedApiRevision === null) { - if (apiRevision.isApproved) { - apiRevision.isLatestApproved = true; - latestApprovedApiRevision = apiRevision; - continue; - } - } + if (gaRevisions.length > 0) { + gaRevisions.sort((a: any, b: any) => b.semanticVersion.compareTo(a.semanticVersion)); + gaRevisions[0].isLatestGA = true; + this.updateTaggedAPIRevisions(apiRevisions, gaRevisions[0]); + return gaRevisions[0]; + } + } - if (currentMainApiRevision === null) { - if (apiRevision.apiRevisionType === 'Automatic') { - apiRevision.isLatestMain = true; - currentMainApiRevision = apiRevision; - continue; - } - } + tagLatestApprovedRevision(apiRevisions: any[]) { + const approvedRevisions : any [] = []; - if (latestReleasedApiRevision === null) { - if (apiRevision.isReleased) { - apiRevision.isLatestReleased = true; - latestReleasedApiRevision = apiRevision; - continue; + for (let apiRevision of apiRevisions) { + if (apiRevision.isApproved && apiRevision.changeHistory.length > 0) { + var approval = apiRevision.changeHistory.find((change: any) => change.changeAction === 'approved'); + if (approval) { + apiRevision.approvedOn = approval.changedOn; + approvedRevisions.push(apiRevision); } } - result.push(apiRevision); } - if (latestGAApiRevision) { - result.unshift(latestGAApiRevision); + if (approvedRevisions.length > 0) { + approvedRevisions.sort((a: any, b: any) => (new Date(b.approvedOn) as any) - (new Date(a.approvedOn) as any)); + approvedRevisions[0].isLatestApproved = true; + this.updateTaggedAPIRevisions(apiRevisions, approvedRevisions[0]); + return approvedRevisions[0]; } + } + + tagCurrentMainRevision(apiRevisions: any[]) { + const automaticRevisions : any [] = []; - if (latestApprovedApiRevision) { - result.unshift(latestApprovedApiRevision); + for (let apiRevision of apiRevisions) { + if (apiRevision.apiRevisionType === 'automatic') { + automaticRevisions.push(apiRevision); + } } - if (currentMainApiRevision) { - result.unshift(currentMainApiRevision); + if (automaticRevisions.length > 0) { + automaticRevisions.sort((a: any, b: any) => (new Date(b.lastUpdatedOn) as any) - (new Date(a.lastUpdatedOn) as any)); + automaticRevisions[0].isLatestMain = true; + this.updateTaggedAPIRevisions(apiRevisions, automaticRevisions[0]); + return automaticRevisions[0]; } + } + + tagLatestReleasedRevision(apiRevisions: any[]) { + const releasedRevisions : any [] = []; - if (latestReleasedApiRevision) { - result.unshift(latestReleasedApiRevision); + for (let apiRevision of apiRevisions) { + if (apiRevision.isReleased) { + releasedRevisions.push(apiRevision); + } + } + + if (releasedRevisions.length > 0) { + releasedRevisions.sort((a: any, b: any) => (new Date(b.releasedOn) as any) - (new Date(a.releasedOn) as any)); + releasedRevisions[0].isLatestReleased = true; + this.updateTaggedAPIRevisions(apiRevisions, releasedRevisions[0]); + return releasedRevisions[0]; } + } - return result; + private updateTaggedAPIRevisions(apiRevisions: any[], taggedApiRevision: any) { + apiRevisions.splice(apiRevisions.indexOf(taggedApiRevision), 1) + apiRevisions.unshift(taggedApiRevision); } } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.ts index c41d8e5fcfb..b5eb8957066 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.ts @@ -5,7 +5,7 @@ import { Datasource, IDatasource, SizeStrategy } from 'ngx-ui-scroll'; import { CommentsService } from 'src/app/_services/comments/comments.service'; import { getQueryParams } from 'src/app/_helpers/router-helpers'; import { ActivatedRoute, Router } from '@angular/router'; -import { SCROLL_TO_NODE_QUERY_PARAM } from 'src/app/_helpers/literal-helpers'; +import { SCROLL_TO_NODE_QUERY_PARAM } from 'src/app/_helpers/common-helpers'; import { CodePanelRowData, CodePanelRowDatatype } from 'src/app/_models/codePanelRowData'; import { StructuredToken } from 'src/app/_models/structuredToken'; import { CommentItemModel, CommentType } from 'src/app/_models/commentItemModel'; diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.ts index 6a72562fe60..924f5b128e6 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.ts @@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChange import { ActivatedRoute, Router } from '@angular/router'; import { InputSwitchOnChangeEvent } from 'primeng/inputswitch'; import { getQueryParams } from 'src/app/_helpers/router-helpers'; -import { mapLanguageAliases } from 'src/app/_helpers/service-helpers'; +import { mapLanguageAliases } from 'src/app/_helpers/common-helpers'; import { Review } from 'src/app/_models/review'; import { APIRevision } from 'src/app/_models/revision'; import { ConfigService } from 'src/app/_services/config/config.service'; diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts index 11bcb13c37d..747bc5d3be1 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { MenuItem, TreeNode } from 'primeng/api'; import { Subject, take, takeUntil } from 'rxjs'; -import { getLanguageCssSafeName } from 'src/app/_helpers/component-helpers'; +import { getLanguageCssSafeName } from 'src/app/_helpers/common-helpers'; import { getQueryParams } from 'src/app/_helpers/router-helpers'; import { Review } from 'src/app/_models/review'; import { APIRevision, ApiTreeBuilderData, CodePanelData, ReviewPageWorkerMessageDirective } from 'src/app/_models/revision'; @@ -12,7 +12,7 @@ import { UserProfileService } from 'src/app/_services/user-profile/user-profile. import { WorkerService } from 'src/app/_services/worker/worker.service'; import { CodePanelComponent } from '../code-panel/code-panel.component'; import { CommentsService } from 'src/app/_services/comments/comments.service'; -import { ACTIVE_API_REVISION_ID_QUERY_PARAM, DIFF_API_REVISION_ID_QUERY_PARAM, DIFF_STYLE_QUERY_PARAM, REVIEW_ID_ROUTE_PARAM, SCROLL_TO_NODE_QUERY_PARAM } from 'src/app/_helpers/literal-helpers'; +import { ACTIVE_API_REVISION_ID_QUERY_PARAM, DIFF_API_REVISION_ID_QUERY_PARAM, DIFF_STYLE_QUERY_PARAM, REVIEW_ID_ROUTE_PARAM, SCROLL_TO_NODE_QUERY_PARAM } from 'src/app/_helpers/common-helpers'; import { CodePanelRowData, CodePanelRowDatatype } from 'src/app/_models/codePanelRowData'; import { UserProfile } from 'src/app/_models/userProfile'; diff --git a/src/dotnet/APIView/ClientSPA/src/app/_helpers/common-helpers.ts b/src/dotnet/APIView/ClientSPA/src/app/_helpers/common-helpers.ts new file mode 100644 index 00000000000..581503a5dee --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_helpers/common-helpers.ts @@ -0,0 +1,29 @@ +export const REVIEW_ID_ROUTE_PARAM = "reviewId"; +export const ACTIVE_API_REVISION_ID_QUERY_PARAM = "activeApiRevisionId"; +export const DIFF_API_REVISION_ID_QUERY_PARAM = "diffApiRevisionId"; +export const DIFF_STYLE_QUERY_PARAM = "diffStyle"; +export const SCROLL_TO_NODE_QUERY_PARAM = "nId"; + +export function getLanguageCssSafeName(language: string): string { + switch (language.toLowerCase()) { + case "c#": + return "csharp"; + case "c++": + return "cplusplus"; + default: + return language.toLowerCase(); + } +} + +export function mapLanguageAliases(languages: Iterable): string[] { + const result: Set = new Set(); + + for (const language of languages) { + if (language === "TypeSpec" || language === "Cadl") { + result.add("Cadl"); + result.add("TypeSpec"); + } + result.add(language); + } + return Array.from(result); +} \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_helpers/component-helpers.ts b/src/dotnet/APIView/ClientSPA/src/app/_helpers/component-helpers.ts deleted file mode 100644 index fa035b73e8a..00000000000 --- a/src/dotnet/APIView/ClientSPA/src/app/_helpers/component-helpers.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function getLanguageCssSafeName(language: string): string { - switch (language.toLowerCase()) { - case "c#": - return "csharp"; - case "c++": - return "cplusplus"; - default: - return language.toLowerCase(); - } -} \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_helpers/literal-helpers.ts b/src/dotnet/APIView/ClientSPA/src/app/_helpers/literal-helpers.ts deleted file mode 100644 index 6d67d61547d..00000000000 --- a/src/dotnet/APIView/ClientSPA/src/app/_helpers/literal-helpers.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const REVIEW_ID_ROUTE_PARAM = "reviewId"; -export const ACTIVE_API_REVISION_ID_QUERY_PARAM = "activeApiRevisionId"; -export const DIFF_API_REVISION_ID_QUERY_PARAM = "diffApiRevisionId"; -export const DIFF_STYLE_QUERY_PARAM = "diffStyle"; -export const SCROLL_TO_NODE_QUERY_PARAM = "nId"; \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_helpers/service-helpers.ts b/src/dotnet/APIView/ClientSPA/src/app/_helpers/service-helpers.ts deleted file mode 100644 index 866f6b3bf33..00000000000 --- a/src/dotnet/APIView/ClientSPA/src/app/_helpers/service-helpers.ts +++ /dev/null @@ -1,13 +0,0 @@ -export function mapLanguageAliases(languages: Iterable): string[] { - const result: Set = new Set(); - - for (const language of languages) { - if (language === "TypeSpec" || language === "Cadl") { - result.add("Cadl"); - result.add("TypeSpec"); - } - result.add(language); - } - return Array.from(result); -} - diff --git a/src/dotnet/APIView/ClientSPA/src/app/_models/azureEngSemanticVersion.ts b/src/dotnet/APIView/ClientSPA/src/app/_models/azureEngSemanticVersion.ts new file mode 100644 index 00000000000..2eca165d09a --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_models/azureEngSemanticVersion.ts @@ -0,0 +1,105 @@ +// Ported from https://github.com/Azure/azure-sdk-tools/blob/main/eng/common/scripts/SemVer.ps1 +export class AzureEngSemanticVersion { + private static SEM_VAR_REGEX = /(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:(?-?)(?[a-zA-Z]+)(?:(?\.?)(?[0-9]{1,8})(?:(?\.?)(?\d{1,3}))?)?)?/; + + major : number = 0; + minor : number = 0; + patch : number = 0 + prereleaseLabelSeparator : string = ''; + prereleaseLabel : string = '' + prereleaseNumberSeparator : string = ''; + buildNumberSeparator : string = ''; + buildNumber : string = ''; + prereleaseNumber : number = 0; + isPrerelease : boolean = false; + versionType : string = ''; + rawVersion : string; + isSemVerFormat : boolean; + defaultPrereleaseLabel : string = ''; + defaultAlphaReleaseLabel : string = ''; + + constructor(version: string, language: string) { + const versionParts = AzureEngSemanticVersion.SEM_VAR_REGEX.exec(version); + + if (versionParts) { + this.isSemVerFormat = true; + this.rawVersion = version; + + this.major = parseInt(versionParts.groups!['major']); + this.minor = parseInt(versionParts.groups!['minor']); + this.patch = parseInt(versionParts.groups!['patch']); + + if (language === "Python") { + this.setupPythonConventions(); + + } else { + this.setupDefaultConventions(); + } + + if (versionParts.groups!['prelabel']) { + this.prereleaseLabel = versionParts.groups!['prelabel'] + this.prereleaseLabelSeparator = versionParts.groups!["presep"] + this.prereleaseNumber = versionParts.groups!["prenumber"] as unknown as number + this.prereleaseNumberSeparator = versionParts.groups!["prenumsep"] + this.isPrerelease = true + this.versionType = "Beta" + this.buildNumberSeparator = versionParts.groups!["buildnumsep"] + this.buildNumber = versionParts.groups!["buildnumber"] ?? '' + } else { + // artifically provide these values for non-prereleases to enable easy sorting of them later than prereleases. + this.prereleaseLabel = "zzz" + this.prereleaseNumber = 99999999 + this.isPrerelease = false + this.versionType = "GA" + if (this.major == 0) { + // Treat initial 0 versions as a prerelease beta's + this.versionType = "Beta" + this.isPrerelease = true + } + else if (this.patch !== 0) { + this.versionType = "Patch" + } + } + } else { + this.rawVersion = version + this.isSemVerFormat = false + } + } + + private setupPythonConventions() : void { + this.prereleaseLabelSeparator = this.prereleaseNumberSeparator = this.buildNumberSeparator = "" + this.defaultPrereleaseLabel = "b" + this.defaultAlphaReleaseLabel = "a" + } + + private setupDefaultConventions() : void { + this.prereleaseLabelSeparator = "-" + this.prereleaseNumberSeparator = "." + this.buildNumberSeparator = "." + this.defaultPrereleaseLabel = "beta" + this.defaultAlphaReleaseLabel = "alpha" + } + + compareTo(other: AzureEngSemanticVersion): number { + if (!(other instanceof AzureEngSemanticVersion)) { + throw new Error(`Cannot compare ${other} with ${this}`); + } + + let ret = this.major - other.major; + if (ret !== 0) return ret; + + ret = this.minor - other.minor; + if (ret !== 0) return ret; + + ret = this.patch - other.patch; + if (ret !== 0) return ret; + + ret = this.prereleaseLabel.localeCompare(other.prereleaseLabel, undefined, { sensitivity: 'base' }); + if (ret !== 0) return ret; + + ret = this.prereleaseNumber - other.prereleaseNumber; + if (ret !== 0) return ret; + + return (this.buildNumber as unknown as number) - (other.buildNumber as unknown as number); + } +} \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_models/tests/azureEngSemanticVersion.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_models/tests/azureEngSemanticVersion.spec.ts new file mode 100644 index 00000000000..1c222ff8439 --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_models/tests/azureEngSemanticVersion.spec.ts @@ -0,0 +1,51 @@ +import { AzureEngSemanticVersion } from "../azureEngSemanticVersion"; + +describe('AzureEngSemanticVersion', () => { + const version = [ + { version: "1.0.1", langugae: "C#" }, + { version: "2.0.0", langugae: "C#" }, + { version: "2.0.0-alpha.20200920", langugae: "C#" }, + { version: "2.0.0-alpha.20200920.1", langugae: "C#" }, + { version: "2.0.0-beta.2", langugae: "C#" }, + { version: "1.0.10", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.03", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.1", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.5", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.2", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.10", langugae: "C#" }, + { version: "2.0.0-beta.1", langugae: "C#" }, + { version: "2.0.0-beta.10", langugae: "C#" }, + { version: "1.0.0", langugae: "C#" }, + { version: "1.0.0b2", langugae: "Python" }, + { version: "1.0.2", langugae: "C#" }, + ]; + + const expectedSort = [ + { version: "2.0.0", langugae: "C#" }, + { version: "2.0.0-beta.10", langugae: "C#" }, + { version: "2.0.0-beta.2", langugae: "C#" }, + { version: "2.0.0-beta.1", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.10", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.5", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.03", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.2", langugae: "C#" }, + { version: "2.0.0-alpha.20201221.1", langugae: "C#" }, + { version: "2.0.0-alpha.20200920.1", langugae: "C#" }, + { version: "2.0.0-alpha.20200920", langugae: "C#" }, + { version: "1.0.10", langugae: "C#" }, + { version: "1.0.2", langugae: "C#" }, + { version: "1.0.1", langugae: "C#" }, + { version: "1.0.0", langugae: "C#" }, + { version: "1.0.0b2", langugae: "Python" }, + ]; + + it('should sort the versions correctly', () => { + version.sort((a: any, b: any) => { + const aVersion = new AzureEngSemanticVersion(a.version, a.langugae); + const bVersion = new AzureEngSemanticVersion(b.version, b.langugae); + return bVersion.compareTo(aVersion); + }); + + expect(version).toEqual(expectedSort); + }); +}); \ No newline at end of file