Path: blob/main/extensions/copilot/src/platform/notebook/test/node/notebookService.spec.ts
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { assert, describe, it, suite } from 'vitest';6import type * as vscode from 'vscode';7import { _hasSupportedNotebooks, EditorAssociation, INotebookEditorContribution, RegisteredEditorPriority } from '../../../../util/common/notebooks';8import { ExtHostNotebookDocumentData } from '../../../../util/common/test/shims/notebookDocument';9import { URI } from '../../../../util/vs/base/common/uri';10import { NotebookData } from '../../../../vscodeTypes';1112describe('NotebookService', () => {13suite('hasSupportedNotebooks', () => {14// all real providers pulled from package.json of various extensions15const jupyterNotebookProvider: INotebookEditorContribution = {16type: 'jupyter-notebook',17displayName: 'Jupyter Notebook',18priority: RegisteredEditorPriority.default,19selector: [{ filenamePattern: '*.ipynb' }]20};21const kustoNotebookProvider: INotebookEditorContribution = {22type: 'kusto-notebook',23displayName: 'Kusto Notebook',24selector: [{ filenamePattern: '*.knb' }]25};26const kustoNotebookProvider2: INotebookEditorContribution = {27type: 'kusto-notebook-kql',28displayName: 'Kusto Notebook',29priority: RegisteredEditorPriority.option,30selector: [{ filenamePattern: '*.kql' }, { filenamePattern: '*.csl' }]31};32const ghinbNotebookProvider: INotebookEditorContribution = {33type: 'github-issues',34displayName: 'GitHub Issues Notebook',35selector: [{ filenamePattern: '*.github-issues' }]36};3738suite('mock tests', () => {39it('should return false when no providers are registered', async () => {40const notebookDocument = ExtHostNotebookDocumentData.fromNotebookData(41URI.file('one.ipynb'),42new NotebookData([]),43'jupyter-notebook'44).document;4546assert.isFalse(47_hasSupportedNotebooks(notebookDocument.uri, [], [], []),48'Notebook with .ipynb extension should not be supported when there are no providers registered.'49);50});5152it('should support untitled notebooks with matching provider', async () => {53const untitledUri: vscode.Uri = URI.from({54scheme: 'untitled',55authority: '',56path: 'Untitled-1.ipynb',57query: 'jupyter-notebook',58fragment: '',59});60const untitledNotebook = ExtHostNotebookDocumentData.fromNotebookData(61untitledUri,62new NotebookData([]),63'jupyter-notebook'64).document;65const provider: INotebookEditorContribution = {66type: 'test-provider',67displayName: 'Test Provider',68priority: RegisteredEditorPriority.default,69selector: ['*.ipynb']70};71assert.isTrue(72_hasSupportedNotebooks(untitledNotebook.uri, [untitledNotebook], [provider], []),73'Untitled notebook with .ipynb extension should be supported by a provider with a matching selector.'74);75});7677it('should support notebooks with basic string selector', async () => {78const provider: INotebookEditorContribution = {79type: 'test-provider',80displayName: 'Test Provider',81priority: RegisteredEditorPriority.default,82selector: ['*.ipynb']83};84const notebookDocument = ExtHostNotebookDocumentData.fromNotebookData(85URI.file('one.ipynb'),86new NotebookData([]),87'jupyter-notebook'88).document;89assert.isTrue(90_hasSupportedNotebooks(notebookDocument.uri, [], [provider], []),91'Notebook with .ipynb extension should be supported by a provider with a basic string selector.'92);93});9495it('should support case-insensitive selector matching for file extensions', async () => {96const provider: INotebookEditorContribution = {97type: 'test-provider',98displayName: 'Test Provider',99priority: RegisteredEditorPriority.default,100selector: ['*.ipynb']101};102const notebookDocument = ExtHostNotebookDocumentData.fromNotebookData(103URI.file('one.IPYNB'),104new NotebookData([]),105'jupyter-notebook'106).document;107assert.isTrue(108_hasSupportedNotebooks(notebookDocument.uri, [], [provider], []),109'Notebook with .IPYNB extension should be supported by a provider due to case-insensitive selectors.'110);111});112113it('should respect include and exclude patterns in provider selector', async () => {114const provider: INotebookEditorContribution = {115type: 'test-provider',116displayName: 'Test Provider',117priority: RegisteredEditorPriority.default,118selector: [{119include: '*.ipynb',120exclude: 'test.*'121}]122};123124// Test file that matches include but not exclude125const valid = ExtHostNotebookDocumentData.fromNotebookData(126URI.file('one.ipynb'),127new NotebookData([]),128'jupyter-notebook'129).document;130assert.isTrue(131_hasSupportedNotebooks(valid.uri, [], [provider], []),132'Notebook matching only the include pattern should be supported.'133);134135// Test file that matches both include and exclude136const excluded = ExtHostNotebookDocumentData.fromNotebookData(137URI.file('test.ipynb'),138new NotebookData([]),139'jupyter-notebook'140).document;141assert.isFalse(142_hasSupportedNotebooks(excluded.uri, [], [provider], []),143'Notebook matching both include and exclude patterns should not be supported.'144);145146// Test file that doesn't match either pattern147const nonMatching = ExtHostNotebookDocumentData.fromNotebookData(148URI.file('one.txt'),149new NotebookData([]),150'jupyter-notebook'151).document;152assert.isFalse(153_hasSupportedNotebooks(nonMatching.uri, [], [provider], []),154'Notebook matching neither include nor exclude pattern should not be supported.'155);156157// Test file in a subdirectory158const subDirValid = ExtHostNotebookDocumentData.fromNotebookData(159URI.file('subdir/one.ipynb'),160new NotebookData([]),161'jupyter-notebook'162).document;163assert.isTrue(164_hasSupportedNotebooks(subDirValid.uri, [], [provider], []),165'Notebook in subdirectory matching include pattern should be supported.'166);167168// Test remote URI169const remoteValid = ExtHostNotebookDocumentData.fromNotebookData(170URI.parse('vscode-remote://ssh-remote+test/one.ipynb'),171new NotebookData([]),172'jupyter-notebook'173).document;174assert.isTrue(175_hasSupportedNotebooks(remoteValid.uri, [], [provider], []),176'Notebook with remote URI matching include pattern should be supported.'177);178});179180it('should respect filenamePattern and excludeFileNamePattern in provider selector', async () => {181const provider: INotebookEditorContribution = {182type: 'test-provider',183displayName: 'Test Provider',184priority: RegisteredEditorPriority.default,185selector: [{186filenamePattern: '*.ipynb',187excludeFileNamePattern: 'test.*'188}]189};190191// Test file that matches include but not exclude192const valid = ExtHostNotebookDocumentData.fromNotebookData(193URI.file('one.ipynb'),194new NotebookData([]),195'jupyter-notebook'196).document;197assert.isTrue(198_hasSupportedNotebooks(valid.uri, [], [provider], []),199'Notebook matching filenamePattern but not excludeFileNamePattern should be supported.'200);201202// Test file that matches both include and exclude203const excluded = ExtHostNotebookDocumentData.fromNotebookData(204URI.file('test.ipynb'),205new NotebookData([]),206'jupyter-notebook'207).document;208assert.isFalse(209_hasSupportedNotebooks(excluded.uri, [], [provider], []),210'Notebook matching both filenamePattern and excludeFileNamePattern should not be supported.'211);212});213214it('should return false when only providers with valid selector have non-default priority', async () => {215const testNotebook = ExtHostNotebookDocumentData.fromNotebookData(216URI.file('test.ipynb'),217new NotebookData([]),218'jupyter-notebook'219).document;220221const providers = [222{223type: 'provider1',224displayName: 'Provider 1',225priority: RegisteredEditorPriority.option,226selector: ['*.ipynb']227},228{229type: 'provider2',230displayName: 'Provider 2',231priority: RegisteredEditorPriority.default,232selector: ['*.other']233}234];235assert.isFalse(236_hasSupportedNotebooks(testNotebook.uri, [], providers, []),237'Notebook with .ipynb extension should not be supported when only non-default priority providers match.'238);239});240241it('should return true when a provider with valid selector has default priority', async () => {242const testNotebook = ExtHostNotebookDocumentData.fromNotebookData(243URI.file('test.ipynb'),244new NotebookData([]),245'jupyter-notebook'246).document;247248const providers = [249{250type: 'provider1',251displayName: 'Provider 1',252priority: RegisteredEditorPriority.default,253selector: ['*.ipynb']254},255{256type: 'provider2',257displayName: 'Provider 2',258priority: RegisteredEditorPriority.default,259selector: ['*.other']260}261];262assert.isTrue(263_hasSupportedNotebooks(testNotebook.uri, [], providers, []),264'Notebook with .ipynb extension should be supported when a provider with default priority matches.'265);266});267268it('should return true when only option-priority providers match but there is a matching editor association', async () => {269const testNotebook = ExtHostNotebookDocumentData.fromNotebookData(270URI.file('test.ipynb'),271new NotebookData([]),272'jupyter-notebook'273).document;274275const associations = [{ viewType: 'option-provider', filenamePattern: '*.ipynb' }];276const providers = [{277type: 'option-provider',278displayName: 'Option Provider',279priority: RegisteredEditorPriority.option,280selector: ['*.ipynb']281}];282assert.isTrue(283_hasSupportedNotebooks(testNotebook.uri, [], providers, associations),284'Notebook with .ipynb extension should be supported when an option-priority provider matches and there is a matching editor association.'285);286});287});288289suite('real extension tests', () => {290it('should return true for providers with no set priority, due to default fallback', async () => {291const ghinbUri: vscode.Uri = URI.from({292scheme: 'file',293authority: '',294path: '_endgame.github-issues',295query: '',296fragment: '',297});298assert.isTrue(299_hasSupportedNotebooks(ghinbUri, [], [ghinbNotebookProvider], []),300'Notebook with .github-issues extension should be supported even when there is no valid editor association.'301);302});303304it('should return true for github issues notebook provider with *.github-issues uri and a valid association', async () => {305const ghinbUri: vscode.Uri = URI.from({306scheme: 'file',307authority: '',308path: '_endgame.github-issues',309query: '',310fragment: '',311});312const ghinbAssociation: EditorAssociation = { viewType: 'github-issues', filenamePattern: '*.github-issues' };313assert.isTrue(314_hasSupportedNotebooks(ghinbUri, [], [ghinbNotebookProvider], [ghinbAssociation]),315'Notebook with .github-issues extension should be supported when there is a valid editor association.'316);317});318319it('should return true for various notebook files with multiple providers and all valid associations', async () => {320const providers = [ghinbNotebookProvider, jupyterNotebookProvider, kustoNotebookProvider, kustoNotebookProvider2];321const associations = [322{ viewType: 'jupyter-notebook', filenamePattern: '*.ipynb' },323{ viewType: 'kusto-notebook', filenamePattern: '*.knb' },324{ viewType: 'kusto-notebook-kql', filenamePattern: '*.kql' },325{ viewType: 'kusto-notebook-kql', filenamePattern: '*.csl' },326{ viewType: 'github-issues', filenamePattern: '*.github-issues' }327];328const testFiles = [329URI.file('test.ipynb'),330URI.file('test.knb'),331URI.file('test.kql'),332URI.file('test.csl'),333URI.file('test.github-issues'),334];335336for (const testFile of testFiles) {337assert.isTrue(338_hasSupportedNotebooks(testFile, [], providers, associations),339`Notebook with extension matching a provider and association should be supported: ${testFile.toString()}`340);341}342});343});344});345346347});348349350