From 0cff268d89845c219f88937e56a43becb41ea1fa Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Thu, 25 Apr 2024 15:23:42 +0200 Subject: [PATCH] fix: trust new notebooks and notebooks with new cells Creating a new notebook or adding a new cell to a notebook should not remove the trust status of the notebook. --- nbclassic/static/notebook/js/cell.js | 13 +++++++++- nbclassic/tests/end_to_end/test_trust.py | 30 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 nbclassic/tests/end_to_end/test_trust.py diff --git a/nbclassic/static/notebook/js/cell.js b/nbclassic/static/notebook/js/cell.js index 4fd4ec442..69d6d32d1 100644 --- a/nbclassic/static/notebook/js/cell.js +++ b/nbclassic/static/notebook/js/cell.js @@ -104,6 +104,14 @@ define([ this.cell_id = utils.uuid(); + // Similar to nbformat, which uses `uuid.uuid4().hex[:8]` + // as recommended by JEP 62. + // We do not check the nbformat version here, as the notebook will report + // the incorrect (4, 1) nbformat version while calling + // insert_cell_below(...) in load_notebook_success (in notebook.js). + // To not break with nbformat < 4.5, we do not include this field in toJSON. + this.id = utils.uuid().slice(0, 8); + // For JS VM engines optimization, attributes should be all set (even // to null) in the constructor, and if possible, if different subclass // have new attributes with same name, they should be created in the @@ -494,7 +502,10 @@ define([ var data = {}; // deepcopy the metadata so copied cells don't share the same object data.metadata = JSON.parse(JSON.stringify(this.metadata)); - if (this.id !== undefined) { + // id's are only introduced in 4.5 and should only be added to 'raw', 'code' and 'markdown' cells + var nbformatSupportsIds = (Jupyter.notebook.nbformat == 4 && Jupyter.notebook.nbformat_minor >= 5) || (Jupyter.notebook.nbformat > 4); + var cellTypeCanIncludeId = this.cell_type == 'raw' || this.cell_type == 'code' || this.cell_type == 'markdown'; + if (nbformatSupportsIds && cellTypeCanIncludeId) { data.id = this.id; } if (data.metadata.deletable) { diff --git a/nbclassic/tests/end_to_end/test_trust.py b/nbclassic/tests/end_to_end/test_trust.py new file mode 100644 index 000000000..f9860326d --- /dev/null +++ b/nbclassic/tests/end_to_end/test_trust.py @@ -0,0 +1,30 @@ +from .utils import EDITOR_PAGE + +trust_test_code = """ +import IPython.display +IPython.display.Javascript("window.javascriptExecuted = true") +""" + + +def test_trust(notebook_frontend): + def save_notebook(): + notebook_frontend.evaluate("() => Jupyter.notebook.save_notebook()", page=EDITOR_PAGE) + + # Add a cell that executes javascript + notebook_frontend.add_cell(index=0, content=trust_test_code) + notebook_frontend.execute_cell(0) + notebook_frontend.wait_for_cell_output(0) + # Make sure the JavaScript executed + assert notebook_frontend.evaluate("() => window.javascriptExecuted", page=EDITOR_PAGE) == True + # Check that we see the 'Trusted' text on the page + trusted = notebook_frontend.locate("#notification_trusted", page=EDITOR_PAGE) + assert trusted.get_inner_text() == "Trusted" + save_notebook() + + # refresh the page, should be trusted + notebook_frontend.reload(EDITOR_PAGE) + notebook_frontend.wait_for_kernel_ready() + trusted = notebook_frontend.locate("#notification_trusted", page=EDITOR_PAGE) + assert trusted.get_inner_text() == "Trusted" + notebook_frontend.wait_for_cell_output(0) + assert notebook_frontend.evaluate("() => window.javascriptExecuted", page=EDITOR_PAGE) == True