mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-17 01:26:32 +00:00
Link resolved for lib/boards manager.
Closes #1442 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
parent
019b2d5588
commit
0c49709f26
@ -334,6 +334,7 @@ import { DeleteSketch } from './contributions/delete-sketch';
|
|||||||
import { UserFields } from './contributions/user-fields';
|
import { UserFields } from './contributions/user-fields';
|
||||||
import { UpdateIndexes } from './contributions/update-indexes';
|
import { UpdateIndexes } from './contributions/update-indexes';
|
||||||
import { InterfaceScale } from './contributions/interface-scale';
|
import { InterfaceScale } from './contributions/interface-scale';
|
||||||
|
import { OpenHandler } from '@theia/core/lib/browser/opener-service';
|
||||||
|
|
||||||
const registerArduinoThemes = () => {
|
const registerArduinoThemes = () => {
|
||||||
const themes: MonacoThemeJson[] = [
|
const themes: MonacoThemeJson[] = [
|
||||||
@ -398,6 +399,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
bind(FrontendApplicationContribution).toService(
|
bind(FrontendApplicationContribution).toService(
|
||||||
LibraryListWidgetFrontendContribution
|
LibraryListWidgetFrontendContribution
|
||||||
);
|
);
|
||||||
|
bind(OpenHandler).toService(LibraryListWidgetFrontendContribution);
|
||||||
|
|
||||||
// Sketch list service
|
// Sketch list service
|
||||||
bind(SketchesService)
|
bind(SketchesService)
|
||||||
@ -464,6 +466,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
bind(FrontendApplicationContribution).toService(
|
bind(FrontendApplicationContribution).toService(
|
||||||
BoardsListWidgetFrontendContribution
|
BoardsListWidgetFrontendContribution
|
||||||
);
|
);
|
||||||
|
bind(OpenHandler).toService(BoardsListWidgetFrontendContribution);
|
||||||
|
|
||||||
// Board select dialog
|
// Board select dialog
|
||||||
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
|
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { injectable } from '@theia/core/shared/inversify';
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
import { BoardsListWidget } from './boards-list-widget';
|
import {
|
||||||
import type {
|
|
||||||
BoardSearch,
|
BoardSearch,
|
||||||
BoardsPackage,
|
BoardsPackage,
|
||||||
} from '../../common/protocol/boards-service';
|
} from '../../common/protocol/boards-service';
|
||||||
|
import { URI } from '../contributions/contribution';
|
||||||
import { ListWidgetFrontendContribution } from '../widgets/component-list/list-widget-frontend-contribution';
|
import { ListWidgetFrontendContribution } from '../widgets/component-list/list-widget-frontend-contribution';
|
||||||
|
import { BoardsListWidget } from './boards-list-widget';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendContribution<
|
export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendContribution<
|
||||||
@ -24,7 +25,16 @@ export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendCont
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
override async initializeLayout(): Promise<void> {
|
protected canParse(uri: URI): boolean {
|
||||||
this.openView();
|
try {
|
||||||
|
BoardSearch.UriParser.parse(uri);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected parse(uri: URI): BoardSearch | undefined {
|
||||||
|
return BoardSearch.UriParser.parse(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import { injectable } from '@theia/core/shared/inversify';
|
|
||||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
|
||||||
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
|
||||||
import { MenuModelRegistry } from '@theia/core';
|
|
||||||
import { LibraryListWidget } from './library-list-widget';
|
|
||||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
|
||||||
import { nls } from '@theia/core/lib/common';
|
import { nls } from '@theia/core/lib/common';
|
||||||
|
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||||
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
|
import { LibraryPackage, LibrarySearch } from '../../common/protocol';
|
||||||
|
import { URI } from '../contributions/contribution';
|
||||||
|
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||||
|
import { ListWidgetFrontendContribution } from '../widgets/component-list/list-widget-frontend-contribution';
|
||||||
|
import { LibraryListWidget } from './library-list-widget';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LibraryListWidgetFrontendContribution
|
export class LibraryListWidgetFrontendContribution extends ListWidgetFrontendContribution<
|
||||||
extends AbstractViewContribution<LibraryListWidget>
|
LibraryPackage,
|
||||||
implements FrontendApplicationContribution
|
LibrarySearch
|
||||||
{
|
> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
widgetId: LibraryListWidget.WIDGET_ID,
|
widgetId: LibraryListWidget.WIDGET_ID,
|
||||||
@ -24,10 +25,6 @@ export class LibraryListWidgetFrontendContribution
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async initializeLayout(): Promise<void> {
|
|
||||||
this.openView();
|
|
||||||
}
|
|
||||||
|
|
||||||
override registerMenus(menus: MenuModelRegistry): void {
|
override registerMenus(menus: MenuModelRegistry): void {
|
||||||
if (this.toggleCommand) {
|
if (this.toggleCommand) {
|
||||||
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||||
@ -40,4 +37,17 @@ export class LibraryListWidgetFrontendContribution
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected canParse(uri: URI): boolean {
|
||||||
|
try {
|
||||||
|
LibrarySearch.UriParser.parse(uri);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected parse(uri: URI): LibrarySearch | undefined {
|
||||||
|
return LibrarySearch.UriParser.parse(uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
import { injectable } from '@theia/core/shared/inversify';
|
|
||||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||||
|
import {
|
||||||
|
OpenerOptions,
|
||||||
|
OpenHandler,
|
||||||
|
} from '@theia/core/lib/browser/opener-service';
|
||||||
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||||
|
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||||
|
import { URI } from '@theia/core/lib/common/uri';
|
||||||
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
|
import { Searchable } from '../../../common/protocol';
|
||||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||||
import { ListWidget } from './list-widget';
|
import { ListWidget } from './list-widget';
|
||||||
import { Searchable } from '../../../common/protocol';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export abstract class ListWidgetFrontendContribution<
|
export abstract class ListWidgetFrontendContribution<
|
||||||
@ -11,14 +17,49 @@ export abstract class ListWidgetFrontendContribution<
|
|||||||
S extends Searchable.Options
|
S extends Searchable.Options
|
||||||
>
|
>
|
||||||
extends AbstractViewContribution<ListWidget<T, S>>
|
extends AbstractViewContribution<ListWidget<T, S>>
|
||||||
implements FrontendApplicationContribution
|
implements FrontendApplicationContribution, OpenHandler
|
||||||
{
|
{
|
||||||
|
readonly id: string = `http-opener-${this.viewId}`;
|
||||||
|
|
||||||
async initializeLayout(): Promise<void> {
|
async initializeLayout(): Promise<void> {
|
||||||
// TS requires at least one method from `FrontendApplicationContribution`.
|
this.openView();
|
||||||
// Expected to be empty.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override registerMenus(): void {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
override registerMenus(_: MenuModelRegistry): void {
|
||||||
// NOOP
|
// NOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
canHandle(uri: URI, _?: OpenerOptions): number {
|
||||||
|
// `500` is the default HTTP opener in Theia. IDE2 has higher priority.
|
||||||
|
// https://github.com/eclipse-theia/theia/blob/b75b6144b0ffea06a549294903c374fa642135e4/packages/core/src/browser/http-open-handler.ts#L39
|
||||||
|
return this.canParse(uri) ? 501 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async open(
|
||||||
|
uri: URI,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
_?: OpenerOptions | undefined
|
||||||
|
): Promise<void> {
|
||||||
|
const searchOptions = this.parse(uri);
|
||||||
|
if (!searchOptions) {
|
||||||
|
console.warn(
|
||||||
|
`Failed to parse URI into a search options. URI: ${uri.toString()}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const widget = await this.openView({
|
||||||
|
activate: true,
|
||||||
|
reveal: true,
|
||||||
|
});
|
||||||
|
if (!widget) {
|
||||||
|
console.warn(`Failed to open view for URI: ${uri.toString()}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
widget.refresh(searchOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract canParse(uri: URI): boolean;
|
||||||
|
protected abstract parse(uri: URI): S | undefined;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,14 @@ import { Searchable } from './searchable';
|
|||||||
import { Installable } from './installable';
|
import { Installable } from './installable';
|
||||||
import { ArduinoComponent } from './arduino-component';
|
import { ArduinoComponent } from './arduino-component';
|
||||||
import { nls } from '@theia/core/lib/common/nls';
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
import { All, Contributed, Partner, Type, Updatable } from '../nls';
|
import {
|
||||||
|
All,
|
||||||
|
Contributed,
|
||||||
|
Partner,
|
||||||
|
Type as TypeLabel,
|
||||||
|
Updatable,
|
||||||
|
} from '../nls';
|
||||||
|
import URI from '@theia/core/lib/common/uri';
|
||||||
|
|
||||||
export type AvailablePorts = Record<string, [Port, Array<Board>]>;
|
export type AvailablePorts = Record<string, [Port, Array<Board>]>;
|
||||||
export namespace AvailablePorts {
|
export namespace AvailablePorts {
|
||||||
@ -151,6 +158,7 @@ export interface BoardSearch extends Searchable.Options {
|
|||||||
readonly type?: BoardSearch.Type;
|
readonly type?: BoardSearch.Type;
|
||||||
}
|
}
|
||||||
export namespace BoardSearch {
|
export namespace BoardSearch {
|
||||||
|
export const Default: BoardSearch = { type: 'All' };
|
||||||
export const TypeLiterals = [
|
export const TypeLiterals = [
|
||||||
'All',
|
'All',
|
||||||
'Updatable',
|
'Updatable',
|
||||||
@ -161,6 +169,11 @@ export namespace BoardSearch {
|
|||||||
'Arduino@Heart',
|
'Arduino@Heart',
|
||||||
] as const;
|
] as const;
|
||||||
export type Type = typeof TypeLiterals[number];
|
export type Type = typeof TypeLiterals[number];
|
||||||
|
export namespace Type {
|
||||||
|
export function is(arg: unknown): arg is Type {
|
||||||
|
return typeof arg === 'string' && TypeLiterals.includes(arg as Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
export const TypeLabels: Record<Type, string> = {
|
export const TypeLabels: Record<Type, string> = {
|
||||||
All: All,
|
All: All,
|
||||||
Updatable: Updatable,
|
Updatable: Updatable,
|
||||||
@ -177,8 +190,41 @@ export namespace BoardSearch {
|
|||||||
keyof Omit<BoardSearch, 'query'>,
|
keyof Omit<BoardSearch, 'query'>,
|
||||||
string
|
string
|
||||||
> = {
|
> = {
|
||||||
type: Type,
|
type: TypeLabel,
|
||||||
};
|
};
|
||||||
|
export namespace UriParser {
|
||||||
|
export const authority = 'boardsmanager';
|
||||||
|
export function parse(uri: URI): BoardSearch | undefined {
|
||||||
|
if (uri.scheme !== 'http') {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid 'scheme'. Expected 'http'. URI was: ${uri.toString()}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (uri.authority !== authority) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid 'authority'. Expected: '${authority}'. URI was: ${uri.toString()}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const segments = Searchable.UriParser.normalizedSegmentsOf(uri);
|
||||||
|
if (segments.length !== 1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let searchOptions: BoardSearch | undefined = undefined;
|
||||||
|
const [type] = segments;
|
||||||
|
if (!type) {
|
||||||
|
searchOptions = BoardSearch.Default;
|
||||||
|
} else if (BoardSearch.Type.is(type)) {
|
||||||
|
searchOptions = { type };
|
||||||
|
}
|
||||||
|
if (searchOptions) {
|
||||||
|
return {
|
||||||
|
...searchOptions,
|
||||||
|
...Searchable.UriParser.parseQuery(uri),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Port {
|
export interface Port {
|
||||||
|
@ -8,9 +8,10 @@ import {
|
|||||||
Partner,
|
Partner,
|
||||||
Recommended,
|
Recommended,
|
||||||
Retired,
|
Retired,
|
||||||
Type,
|
Type as TypeLabel,
|
||||||
Updatable,
|
Updatable,
|
||||||
} from '../nls';
|
} from '../nls';
|
||||||
|
import URI from '@theia/core/lib/common/uri';
|
||||||
|
|
||||||
export const LibraryServicePath = '/services/library-service';
|
export const LibraryServicePath = '/services/library-service';
|
||||||
export const LibraryService = Symbol('LibraryService');
|
export const LibraryService = Symbol('LibraryService');
|
||||||
@ -55,6 +56,7 @@ export interface LibrarySearch extends Searchable.Options {
|
|||||||
readonly topic?: LibrarySearch.Topic;
|
readonly topic?: LibrarySearch.Topic;
|
||||||
}
|
}
|
||||||
export namespace LibrarySearch {
|
export namespace LibrarySearch {
|
||||||
|
export const Default: LibrarySearch = { type: 'All', topic: 'All' };
|
||||||
export const TypeLiterals = [
|
export const TypeLiterals = [
|
||||||
'All',
|
'All',
|
||||||
'Updatable',
|
'Updatable',
|
||||||
@ -66,6 +68,11 @@ export namespace LibrarySearch {
|
|||||||
'Retired',
|
'Retired',
|
||||||
] as const;
|
] as const;
|
||||||
export type Type = typeof TypeLiterals[number];
|
export type Type = typeof TypeLiterals[number];
|
||||||
|
export namespace Type {
|
||||||
|
export function is(arg: unknown): arg is Type {
|
||||||
|
return typeof arg === 'string' && TypeLiterals.includes(arg as Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
export const TypeLabels: Record<Type, string> = {
|
export const TypeLabels: Record<Type, string> = {
|
||||||
All: All,
|
All: All,
|
||||||
Updatable: Updatable,
|
Updatable: Updatable,
|
||||||
@ -90,6 +97,11 @@ export namespace LibrarySearch {
|
|||||||
'Uncategorized',
|
'Uncategorized',
|
||||||
] as const;
|
] as const;
|
||||||
export type Topic = typeof TopicLiterals[number];
|
export type Topic = typeof TopicLiterals[number];
|
||||||
|
export namespace Topic {
|
||||||
|
export function is(arg: unknown): arg is Topic {
|
||||||
|
return typeof arg === 'string' && TopicLiterals.includes(arg as Topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
export const TopicLabels: Record<Topic, string> = {
|
export const TopicLabels: Record<Topic, string> = {
|
||||||
All: All,
|
All: All,
|
||||||
Communication: nls.localize(
|
Communication: nls.localize(
|
||||||
@ -126,8 +138,60 @@ export namespace LibrarySearch {
|
|||||||
string
|
string
|
||||||
> = {
|
> = {
|
||||||
topic: nls.localize('arduino/librarySearchProperty/topic', 'Topic'),
|
topic: nls.localize('arduino/librarySearchProperty/topic', 'Topic'),
|
||||||
type: Type,
|
type: TypeLabel,
|
||||||
};
|
};
|
||||||
|
export namespace UriParser {
|
||||||
|
export const authority = 'librarymanager';
|
||||||
|
export function parse(uri: URI): LibrarySearch | undefined {
|
||||||
|
if (uri.scheme !== 'http') {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid 'scheme'. Expected 'http'. URI was: ${uri.toString()}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (uri.authority !== authority) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid 'authority'. Expected: '${authority}'. URI was: ${uri.toString()}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const segments = Searchable.UriParser.normalizedSegmentsOf(uri);
|
||||||
|
// Special magic handling for `Signal Input/Output`.
|
||||||
|
// TODO: IDE2 deserves a better lib/boards URL spec.
|
||||||
|
// https://github.com/arduino/arduino-ide/issues/1442#issuecomment-1252136377
|
||||||
|
if (segments.length === 3) {
|
||||||
|
const [type, topicHead, topicTail] = segments;
|
||||||
|
const maybeTopic = `${topicHead}/${topicTail}`;
|
||||||
|
if (
|
||||||
|
LibrarySearch.Topic.is(maybeTopic) &&
|
||||||
|
maybeTopic === 'Signal Input/Output' &&
|
||||||
|
LibrarySearch.Type.is(type)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
topic: maybeTopic,
|
||||||
|
...Searchable.UriParser.parseQuery(uri),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let searchOptions: LibrarySearch | undefined = undefined;
|
||||||
|
const [type, topic] = segments;
|
||||||
|
if (!type && !topic) {
|
||||||
|
searchOptions = LibrarySearch.Default;
|
||||||
|
} else if (LibrarySearch.Type.is(type)) {
|
||||||
|
if (!topic) {
|
||||||
|
searchOptions = { ...LibrarySearch.Default, type };
|
||||||
|
} else if (LibrarySearch.Topic.is(topic)) {
|
||||||
|
searchOptions = { type, topic };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchOptions) {
|
||||||
|
return {
|
||||||
|
...searchOptions,
|
||||||
|
...Searchable.UriParser.parseQuery(uri),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace LibraryService {
|
export namespace LibraryService {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import URI from '@theia/core/lib/common/uri';
|
||||||
|
|
||||||
export interface Searchable<T, O extends Searchable.Options> {
|
export interface Searchable<T, O extends Searchable.Options> {
|
||||||
search(options: O): Promise<T[]>;
|
search(options: O): Promise<T[]>;
|
||||||
}
|
}
|
||||||
@ -8,4 +10,24 @@ export namespace Searchable {
|
|||||||
*/
|
*/
|
||||||
readonly query?: string;
|
readonly query?: string;
|
||||||
}
|
}
|
||||||
|
export namespace UriParser {
|
||||||
|
/**
|
||||||
|
* Parses the `URI#fragment` into a query term.
|
||||||
|
*/
|
||||||
|
export function parseQuery(uri: URI): { query: string } {
|
||||||
|
return { query: uri.fragment };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Splits the `URI#path#toString` on the `/` POSIX separator into decoded segments. The first, empty segment representing the root is omitted.
|
||||||
|
* Examples:
|
||||||
|
* - `/` -> `['']`
|
||||||
|
* - `/All` -> `['All']`
|
||||||
|
* - `/All/Device%20Control` -> `['All', 'Device Control']`
|
||||||
|
* - `/All/Display` -> `['All', 'Display']`
|
||||||
|
* - `/Updatable/Signal%20Input%2FOutput` -> `['Updatable', 'Signal Input', 'Output']` (**caveat**!)
|
||||||
|
*/
|
||||||
|
export function normalizedSegmentsOf(uri: URI): string[] {
|
||||||
|
return uri.path.toString().split('/').slice(1).map(decodeURIComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
136
arduino-ide-extension/src/test/common/searchable.test.ts
Normal file
136
arduino-ide-extension/src/test/common/searchable.test.ts
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import URI from '@theia/core/lib/common/uri';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { BoardSearch, LibrarySearch, Searchable } from '../../common/protocol';
|
||||||
|
|
||||||
|
interface Expectation<S extends Searchable.Options> {
|
||||||
|
readonly uri: string;
|
||||||
|
readonly expected: S | undefined | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('searchable', () => {
|
||||||
|
describe('parse', () => {
|
||||||
|
describe(BoardSearch.UriParser.authority, () => {
|
||||||
|
(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
uri: 'http://boardsmanager#SAMD',
|
||||||
|
expected: { query: 'SAMD', type: 'All' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://boardsmanager/Arduino%40Heart#littleBits',
|
||||||
|
expected: { query: 'littleBits', type: 'Arduino@Heart' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://boardsmanager/too/many/segments#invalidPath',
|
||||||
|
expected: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://boardsmanager/random#invalidPath',
|
||||||
|
expected: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'https://boardsmanager/#invalidScheme',
|
||||||
|
expected: `Invalid 'scheme'. Expected 'http'. URI was: https://boardsmanager/#invalidScheme.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://librarymanager/#invalidAuthority',
|
||||||
|
expected: `Invalid 'authority'. Expected: 'boardsmanager'. URI was: http://librarymanager/#invalidAuthority.`,
|
||||||
|
},
|
||||||
|
] as Expectation<BoardSearch>[]
|
||||||
|
).map((expectation) => toIt(expectation, BoardSearch.UriParser.parse));
|
||||||
|
});
|
||||||
|
describe(LibrarySearch.UriParser.authority, () => {
|
||||||
|
(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
uri: 'http://librarymanager#WiFiNINA',
|
||||||
|
expected: { query: 'WiFiNINA', type: 'All', topic: 'All' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://librarymanager/All/Device%20Control#Servo',
|
||||||
|
expected: {
|
||||||
|
query: 'Servo',
|
||||||
|
type: 'All',
|
||||||
|
topic: 'Device Control',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://librarymanager/All/Display#SparkFun',
|
||||||
|
expected: {
|
||||||
|
query: 'SparkFun',
|
||||||
|
type: 'All',
|
||||||
|
topic: 'Display',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://librarymanager/Updatable/Display#SparkFun',
|
||||||
|
expected: {
|
||||||
|
query: 'SparkFun',
|
||||||
|
type: 'Updatable',
|
||||||
|
topic: 'Display',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://librarymanager/All/Signal%20Input%2FOutput#debouncer',
|
||||||
|
expected: {
|
||||||
|
query: 'debouncer',
|
||||||
|
type: 'All',
|
||||||
|
topic: 'Signal Input/Output',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://librarymanager/too/many/segments#invalidPath',
|
||||||
|
expected: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://librarymanager/absent/invalid#invalidPath',
|
||||||
|
expected: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'https://librarymanager/#invalidScheme',
|
||||||
|
expected: `Invalid 'scheme'. Expected 'http'. URI was: https://librarymanager/#invalidScheme.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: 'http://boardsmanager/#invalidAuthority',
|
||||||
|
expected: `Invalid 'authority'. Expected: 'librarymanager'. URI was: http://boardsmanager/#invalidAuthority.`,
|
||||||
|
},
|
||||||
|
] as Expectation<LibrarySearch>[]
|
||||||
|
).map((expectation) => toIt(expectation, LibrarySearch.UriParser.parse));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function toIt<S extends Searchable.Options>(
|
||||||
|
{ uri, expected }: Expectation<S>,
|
||||||
|
run: (uri: URI) => Searchable.Options | undefined
|
||||||
|
): Mocha.Test {
|
||||||
|
return it(`should ${
|
||||||
|
typeof expected === 'string'
|
||||||
|
? `fail to parse '${uri}'`
|
||||||
|
: !expected
|
||||||
|
? `not parse '${uri}'`
|
||||||
|
: `parse '${uri}' to ${JSON.stringify(expected)}`
|
||||||
|
}`, () => {
|
||||||
|
if (typeof expected === 'string') {
|
||||||
|
try {
|
||||||
|
run(new URI(uri));
|
||||||
|
expect.fail(
|
||||||
|
`Expected an error with message '${expected}' when parsing URI: ${uri}.`
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.instanceOf(Error);
|
||||||
|
expect(err.message).to.be.equal(expected);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const actual = run(new URI(uri));
|
||||||
|
if (!expected) {
|
||||||
|
expect(actual).to.be.undefined;
|
||||||
|
} else {
|
||||||
|
expect(actual).to.be.deep.equal(
|
||||||
|
expected,
|
||||||
|
`Was: ${JSON.stringify(actual)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user