Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
13 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,47 @@ jobs:
- name: Check licenses
run: npm run check-licenses

integration-tests:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FilipPyrek pls open a pr doing just this for the sake of integrations tests already in main and assign me as a reviewer.

name: Integration Tests
runs-on: ubicloud
timeout-minutes: 30
permissions:
id-token: write
contents: read
packages: read
Comment on lines +150 to +153
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DevinAI What do you need the permissions for? I thought it's defined at the top-level

steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5

- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
cache: 'npm'
node-version: ${{ env.NODE_VERSION }}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DevinAI
In .github/workflows/ci.yml around line 162, the workflow references an
undefined environment variable node-version: ${{ env.NODE_VERSION }}; replace it
with node-version-file: '.nvmrc' to match other jobs (or alternatively define
env.NODE_VERSION at workflow level), ensuring the job uses the .nvmrc-specified
Node version.

registry-url: 'https://npm.pkg.github.com'
scope: '@deepnote'

- name: Setup Python
uses: actions/setup-python@v5
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DevinAI
.github/workflows/ci.yml around line 167: the workflow uses a tag reference for
actions/setup-python@v5 while other actions are pinned to commit SHAs; replace
the tag with the specific commit SHA for the actions/setup-python v5 release to
match the repository's supply-chain security policy. Fetch the canonical commit
SHA for the desired v5 version from the actions/setup-python GitHub repo, update
the uses line to use that SHA (e.g. actions/setup-python@), and
ensure the rest of the workflow follows the same pinning convention.

with:
python-version: '3.12'

- name: Install Jupyter
run: python3 -m pip install jupyter ipykernel
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DevinAI is it necessary to install jupyter and ipykernel. I thought that the extension will install those. If it's not necessary, remove it.


- name: Install dependencies
run: npm ci --prefer-offline --no-audit
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Compile TypeScript
run: npm run compile

- name: Run integration tests
run: xvfb-run -a -s "-screen 0 1024x768x24" npm run test:integration
env:
VSC_JUPYTER_CI_TEST_GREP: 'Deepnote Integration Tests'

check_licenses:
name: Check Licenses
runs-on: ubuntu-latest
Expand Down
3 changes: 2 additions & 1 deletion src/notebooks/deepnote/deepnoteExplorerView.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { injectable, inject } from 'inversify';
import { commands, window, workspace, type TreeView, Uri, l10n } from 'vscode';
import * as yaml from 'js-yaml';
import { convertIpynbFilesToDeepnoteFile } from '@deepnote/convert';

import { IExtensionContext } from '../../platform/common/types';
import { IDeepnoteNotebookManager } from '../types';
Expand Down Expand Up @@ -357,6 +356,7 @@ export class DeepnoteExplorerView {
const outputFileName = `${projectName}.deepnote`;
const outputPath = Uri.joinPath(workspaceFolder.uri, outputFileName).path;

const { convertIpynbFilesToDeepnoteFile } = await import('@deepnote/convert');
await convertIpynbFilesToDeepnoteFile(inputFilePaths, {
outputPath: outputPath,
projectName: projectName
Expand Down Expand Up @@ -429,6 +429,7 @@ export class DeepnoteExplorerView {
// File doesn't exist, continue
}

const { convertIpynbFilesToDeepnoteFile } = await import('@deepnote/convert');
await convertIpynbFilesToDeepnoteFile(inputFilePaths, {
outputPath: outputUri.path,
projectName: projectName
Expand Down
76 changes: 76 additions & 0 deletions src/test/datascience/notebook/deepnote.vscode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
import { assert } from 'chai';
import * as path from '../../../platform/vscode-path/path';
import { Uri, workspace } from 'vscode';
import { IDisposable } from '../../../platform/common/types';
import { captureScreenShot, IExtensionTestApi } from '../../common.node';
import { EXTENSION_ROOT_DIR_FOR_TESTS, initialize } from '../../initialize.node';
import { closeNotebooksAndCleanUpAfterTests } from './helper.node';
import { logger } from '../../../platform/logging';
import { IDeepnoteNotebookManager } from '../../../notebooks/types';

/* eslint-disable @typescript-eslint/no-explicit-any, no-invalid-this */
suite('Deepnote Integration Tests @kernelCore', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Misleading suite tag.

Suite is tagged @kernelCore but tests don't exercise kernels—only file loading and metadata. Consider @deepnote or remove the kernel tag.

-suite('Deepnote Integration Tests @kernelCore', function () {
+suite('Deepnote Integration Tests', function () {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
suite('Deepnote Integration Tests @kernelCore', function () {
suite('Deepnote Integration Tests', function () {
🤖 Prompt for AI Agents
In src/test/datascience/notebook/deepnote.vscode.test.ts around line 12, the
test suite is incorrectly tagged as '@kernelCore' despite not exercising kernel
functionality; update the suite tag to a more accurate label such as '@deepnote'
(or remove the kernel-related tag entirely) so the metadata reflects the tests'
scope and test filtering remains correct.

let api: IExtensionTestApi;
const disposables: IDisposable[] = [];
const deepnoteFilePath = Uri.file(
path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', 'datascience', 'notebook', 'test.deepnote')
);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DevinAI In src/test/datascience/notebook/deepnote.vscode.test.ts around lines 24 to 26,
the test builds a fixture URI using path.join wrapped with Uri.file which is not
platform-safe; replace that with Uri.joinPath(EXTENSION_ROOT_DIR_FOR_TESTS_URI,
'src', 'test', 'datascience', 'notebook', 'test.deepnote') (or construct a Uri
for EXTENSION_ROOT_DIR_FOR_TESTS first) so the URI is composed using VS Code's
platform-safe API instead of path.join.

this.timeout(240_000);

suiteSetup(async function () {
logger.info('Suite Setup VS Code Notebook - Deepnote Integration');
this.timeout(240_000);
try {
api = await initialize();
logger.info('Suite Setup (completed)');
} catch (e) {
logger.error('Suite Setup (failed) - Deepnote Integration', e);
await captureScreenShot('deepnote-suite');
throw e;
}
});

setup(function () {
logger.info(`Start Test (completed) ${this.currentTest?.title}`);
});

teardown(async function () {
if (this.currentTest?.isFailed()) {
await captureScreenShot(this);
}
logger.info(`Ended Test (completed) ${this.currentTest?.title}`);
});

suiteTeardown(() => closeNotebooksAndCleanUpAfterTests(disposables));

test('Load .deepnote file', async function () {
logger.debug('Test: Load .deepnote file - starting');

const notebookManager = api.serviceContainer.get<IDeepnoteNotebookManager>(IDeepnoteNotebookManager);
assert.isOk(notebookManager, 'Notebook manager should be available');

notebookManager.selectNotebookForProject('test-project-id', 'main-notebook-id');

const nbDocument = await workspace.openNotebookDocument(deepnoteFilePath);

logger.debug(`Opened notebook with type: ${nbDocument.notebookType}, cells: ${nbDocument.cellCount}`);

assert.equal(nbDocument.notebookType, 'deepnote', 'Notebook type should be deepnote');
assert.isTrue(nbDocument.cellCount > 0, 'Notebook should have cells');

assert.equal(nbDocument.metadata?.deepnoteProjectId, 'test-project-id', 'Project ID should match');
assert.equal(nbDocument.metadata?.deepnoteNotebookId, 'main-notebook-id', 'Notebook ID should match');

logger.debug('Test: Load .deepnote file - completed');
});

test('Extension services are available', async function () {
logger.debug('Test: Extension services are available - starting');

const notebookManager = api.serviceContainer.get<IDeepnoteNotebookManager>(IDeepnoteNotebookManager);
assert.isOk(notebookManager, 'Notebook manager should be available');

logger.debug('Test: Extension services are available - completed');
});
});
39 changes: 39 additions & 0 deletions src/test/datascience/notebook/test.deepnote
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
metadata:
createdAt: '2025-01-01T00:00:00.000Z'
modifiedAt: '2025-01-01T00:00:00.000Z'
project:
id: test-project-id
name: Test Project
initNotebookId: init-notebook-id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Init notebook execution not tested.

PR objectives require verifying that the init notebook (referenced by initNotebookId) executes on kernel start. The test suite doesn't validate this behavior—it only tests manually appended cells.

Verify whether init notebook auto-execution is implemented and add a test that:

  1. Starts the kernel
  2. Checks that init-block-1 was executed (e.g., by inspecting kernel execution history or checking for "Init notebook executed" in outputs)

🏁 Script executed:

#!/bin/bash
# Search for init notebook execution logic in the codebase
rg -nP --type=ts 'initNotebook' -C3

Length of output: 10708



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for test files related to init notebook execution
fd -e "test.ts" -e "test.js" -e "spec.ts" | xargs rg -l "initNotebook|InitNotebook" 2>/dev/null | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if test.deepnote file exists and what it contains
find . -name "test.deepnote" -type f -exec cat {} \;

Length of output: 990


🏁 Script executed:

#!/bin/bash
# Search for any test references to deepnoteKernelAutoSelector or deepnoteInitNotebookRunner
fd -e "test.ts" -e "test.js" -e "spec.ts" | xargs rg -l "deepnoteKernelAutoSelector|deepnoteInitNotebookRunner" 2>/dev/null

Length of output: 0


Add tests for init notebook auto-execution on kernel start.

Init notebook execution is implemented in deepnoteInitNotebookRunner.node.ts and triggered by deepnoteKernelAutoSelector.node.ts on kernel start. However, no tests currently validate this behavior. Add a test that:

  1. Starts a kernel for a project with initNotebookId
  2. Verifies the init notebook's code block (init-block-1) executes
  3. Confirms output contains "Init notebook executed"
🤖 Prompt for AI Agents
In src/test/datascience/notebook/test.deepnote around line 7, there is no test
that verifies init notebook auto-execution when a kernel starts; add a
unit/integration test that (1) creates or loads a project config with
initNotebookId set to "init-notebook-id", (2) starts a kernel for that project
(using the same kernel start flow exercised by
deepnoteKernelAutoSelector.node.ts), (3) waits for the init notebook runner
(deepnoteInitNotebookRunner.node.ts) to execute the init notebook code block
with id "init-block-1", and (4) asserts the kernel output contains the string
"Init notebook executed"; ensure the test hooks into the same event/notification
mechanisms used in production (or stubs/mocks them) and cleans up the
kernel/session after completion.

notebooks:
- id: init-notebook-id
name: Init Notebook
blocks:
- id: init-block-1
type: code
content: |
# This is the init notebook
import sys
print("Init notebook executed")
sortingKey: '0001'
- id: main-notebook-id
name: Main Notebook
blocks:
- id: block-1
type: code
content: |
print("Hello World")
sortingKey: '0001'
- id: block-2
type: code
content: |
x = 42
print(f"The answer is {x}")
sortingKey: '0002'
- id: block-3
type: markdown
content: |
# Test Markdown
This is a test markdown block.
sortingKey: '0003'
version: '1.0'
Loading