Bladeren bron

Add initial lab extensions test files

Steven Silvester 8 jaren geleden
bovenliggende
commit
c45e782dea
3 gewijzigde bestanden met toevoegingen van 478 en 1 verwijderingen
  1. 1 1
      jupyterlab/labextensions.py
  2. 10 0
      jupyterlab/tests/mockextension/index.js
  3. 467 0
      jupyterlab/tests/test_labextensions.py

+ 1 - 1
jupyterlab/labextensions.py

@@ -839,7 +839,7 @@ class ListLabExtensionsApp(BaseLabExtensionApp):
             cm = BaseJSONConfigManager(parent=self, config_dir=config_dir)
             data = cm.get("jupyter_notebook_config")
             lab_extensions = (
-                data.setdefault("NotebookApp", {})
+                data.setdefault("LabApp", {})
                 .setdefault("lab_extensions", {})
             )
             if lab_extensions:

+ 10 - 0
jupyterlab/tests/mockextension/index.js

@@ -0,0 +1,10 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+module.exports = {
+    id: 'mock-extension',
+    autoStart: true,
+    activate: function(application) {
+        window.commands = application.commands;
+    }
+}

+ 467 - 0
jupyterlab/tests/test_labextensions.py

@@ -0,0 +1,467 @@
+# coding: utf-8
+"""Test installation of notebook extensions"""
+
+# Copyright (c) Jupyter Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import glob
+import os
+import sys
+import tarfile
+import zipfile
+from io import BytesIO, StringIO
+from os.path import basename, join as pjoin
+from traitlets.tests.utils import check_help_all_output
+from unittest import TestCase
+
+try:
+    from unittest.mock import patch
+except ImportError:
+    from mock import patch # py2
+
+import ipython_genutils.testing.decorators as dec
+from ipython_genutils import py3compat
+from ipython_genutils.tempdir import TemporaryDirectory
+from jupyterlab import labextensions
+from jupyterlab.labextensions import (install_labextension, check_labextension,
+    enable_labextension, disable_labextension,
+    install_labextension_python, uninstall_labextension_python,
+    enable_labextension_python, disable_labextension_python, _get_config_dir,
+    validate_labextension, validate_labextension_python
+)
+
+from traitlets.config.manager import BaseJSONConfigManager
+
+
+def touch(file, mtime=None):
+    """ensure a file exists, and set its modification time
+    
+    returns the modification time of the file
+    """
+    open(file, 'a').close()
+    # set explicit mtime
+    if mtime:
+        atime = os.stat(file).st_atime
+        os.utime(file, (atime, mtime))
+    return os.stat(file).st_mtime
+
+
+def test_help_output():
+    check_help_all_output('jupyterlab.labextensions')
+    check_help_all_output('jupyterlab.labextensions', ['enable'])
+    check_help_all_output('jupyterlab.labextensions', ['disable'])
+    check_help_all_output('jupyterlab.labextensions', ['install'])
+    check_help_all_output('jupyterlab.labextensions', ['uninstall'])
+
+
+class TestInstallLabExtension(TestCase):
+    
+    def tempdir(self):
+        td = TemporaryDirectory()
+        self.tempdirs.append(td)
+        return py3compat.cast_unicode(td.name)
+
+    def setUp(self):
+        # Any TemporaryDirectory objects appended to this list will be cleaned
+        # up at the end of the test run.
+        self.tempdirs = []
+
+        @self.addCleanup
+        def cleanup_tempdirs():
+            for d in self.tempdirs:
+                d.cleanup()
+
+        self.src = self.tempdir()
+        self.files = files = [
+            pjoin(u'ƒile'),
+            pjoin(u'∂ir', u'ƒile1'),
+            pjoin(u'∂ir', u'∂ir2', u'ƒile2'),
+        ]
+        for file in files:
+            fullpath = os.path.join(self.src, file)
+            parent = os.path.dirname(fullpath)
+            if not os.path.exists(parent):
+                os.makedirs(parent)
+            touch(fullpath)
+
+        self.test_dir = self.tempdir()
+        self.data_dir = os.path.join(self.test_dir, 'data')
+        self.config_dir = os.path.join(self.test_dir, 'config')
+        self.system_data_dir = os.path.join(self.test_dir, 'system_data')
+        self.system_path = [self.system_data_dir]
+        self.system_labext = os.path.join(self.system_data_dir, 'labextensions')
+
+        # Patch out os.environ so that tests are isolated from the real OS
+        # environment.
+        self.patch_env = patch.dict('os.environ', {
+            'JUPYTER_CONFIG_DIR': self.config_dir,
+            'JUPYTER_DATA_DIR': self.data_dir,
+        })
+        self.patch_env.start()
+        self.addCleanup(self.patch_env.stop)
+
+        # Patch out the system path os that we consistently use our own
+        # temporary directory instead.
+        self.patch_system_path = patch.object(
+            labextensions, 'SYSTEM_JUPYTER_PATH', self.system_path
+        )
+        self.patch_system_path.start()
+        self.addCleanup(self.patch_system_path.stop)
+
+    def assert_dir_exists(self, path):
+        if not os.path.exists(path):
+            do_exist = os.listdir(os.path.dirname(path))
+            self.fail(u"%s should exist (found %s)" % (path, do_exist))
+    
+    def assert_not_dir_exists(self, path):
+        if os.path.exists(path):
+            self.fail(u"%s should not exist" % path)
+    
+    def assert_installed(self, relative_path, user=False):
+        if user:
+            labext = pjoin(self.data_dir, u'labextensions')
+        else:
+            labext = self.system_labext
+        self.assert_dir_exists(
+            pjoin(labext, relative_path)
+        )
+    
+    def assert_not_installed(self, relative_path, user=False):
+        if user:
+            labext = pjoin(self.data_dir, u'labextensions')
+        else:
+            labext = self.system_labext
+        self.assert_not_dir_exists(
+            pjoin(labext, relative_path)
+        )
+    
+    def test_create_data_dir(self):
+        """install_labextension when data_dir doesn't exist"""
+        with TemporaryDirectory() as td:
+            data_dir = os.path.join(td, self.data_dir)
+            with patch.dict('os.environ', {
+                'JUPYTER_DATA_DIR': data_dir,
+            }):
+                install_labextension(self.src, user=True)
+                self.assert_dir_exists(data_dir)
+                for file in self.files:
+                    self.assert_installed(
+                        pjoin(basename(self.src), file),
+                        user=True,
+                    )
+    
+    def test_create_labextensions_user(self):
+        with TemporaryDirectory() as td:
+            install_labextension(self.src, user=True)
+            self.assert_installed(
+                pjoin(basename(self.src), u'ƒile'),
+                user=True
+            )
+    
+    def test_create_labextensions_system(self):
+        with TemporaryDirectory() as td:
+            self.system_labext = pjoin(td, u'labextensions')
+            with patch.object(labextensions, 'SYSTEM_JUPYTER_PATH', [td]):
+                install_labextension(self.src, user=False)
+                self.assert_installed(
+                    pjoin(basename(self.src), u'ƒile'),
+                    user=False
+                )
+    
+    def test_single_file(self):
+        file = self.files[0]
+        install_labextension(pjoin(self.src, file))
+        self.assert_installed(file)
+    
+    def test_single_dir(self):
+        d = u'∂ir'
+        install_labextension(pjoin(self.src, d))
+        self.assert_installed(self.files[-1])
+    
+
+    def test_destination_file(self):
+        file = self.files[0]
+        install_labextension(pjoin(self.src, file), destination = u'ƒiledest')
+        self.assert_installed(u'ƒiledest')
+
+    def test_destination_dir(self):
+        d = u'∂ir'
+        install_labextension(pjoin(self.src, d), destination = u'ƒiledest2')
+        self.assert_installed(pjoin(u'ƒiledest2', u'∂ir2', u'ƒile2'))
+    
+    def test_install_labextension(self):
+        with self.assertRaises(TypeError):
+            install_labextension(glob.glob(pjoin(self.src, '*')))
+    
+    def test_overwrite_file(self):
+        with TemporaryDirectory() as d:
+            fname = u'ƒ.js'
+            src = pjoin(d, fname)
+            with open(src, 'w') as f:
+                f.write('first')
+            mtime = touch(src)
+            dest = pjoin(self.system_labext, fname)
+            install_labextension(src)
+            with open(src, 'w') as f:
+                f.write('overwrite')
+            mtime = touch(src, mtime - 100)
+            install_labextension(src, overwrite=True)
+            with open(dest) as f:
+                self.assertEqual(f.read(), 'overwrite')
+    
+    def test_overwrite_dir(self):
+        with TemporaryDirectory() as src:
+            base = basename(src)
+            fname = u'ƒ.js'
+            touch(pjoin(src, fname))
+            install_labextension(src)
+            self.assert_installed(pjoin(base, fname))
+            os.remove(pjoin(src, fname))
+            fname2 = u'∂.js'
+            touch(pjoin(src, fname2))
+            install_labextension(src, overwrite=True)
+            self.assert_installed(pjoin(base, fname2))
+            self.assert_not_installed(pjoin(base, fname))
+    
+    def test_update_file(self):
+        with TemporaryDirectory() as d:
+            fname = u'ƒ.js'
+            src = pjoin(d, fname)
+            with open(src, 'w') as f:
+                f.write('first')
+            mtime = touch(src)
+            install_labextension(src)
+            self.assert_installed(fname)
+            dest = pjoin(self.system_labext, fname)
+            os.stat(dest).st_mtime
+            with open(src, 'w') as f:
+                f.write('overwrite')
+            touch(src, mtime + 10)
+            install_labextension(src)
+            with open(dest) as f:
+                self.assertEqual(f.read(), 'overwrite')
+    
+    def test_skip_old_file(self):
+        with TemporaryDirectory() as d:
+            fname = u'ƒ.js'
+            src = pjoin(d, fname)
+            mtime = touch(src)
+            install_labextension(src)
+            self.assert_installed(fname)
+            dest = pjoin(self.system_labext, fname)
+            old_mtime = os.stat(dest).st_mtime
+            
+            mtime = touch(src, mtime - 100)
+            install_labextension(src)
+            new_mtime = os.stat(dest).st_mtime
+            self.assertEqual(new_mtime, old_mtime)
+
+    def test_quiet(self):
+        stdout = StringIO()
+        stderr = StringIO()
+        with patch.object(sys, 'stdout', stdout), \
+             patch.object(sys, 'stderr', stderr):
+            install_labextension(self.src)
+        self.assertEqual(stdout.getvalue(), '')
+        self.assertEqual(stderr.getvalue(), '')
+    
+    def test_check_labextension(self):
+        with TemporaryDirectory() as d:
+            f = u'ƒ.js'
+            src = pjoin(d, f)
+            touch(src)
+            install_labextension(src, user=True)
+        
+        assert check_labextension(f, user=True)
+        assert check_labextension([f], user=True)
+        assert not check_labextension([f, pjoin('dne', f)], user=True)
+    
+    @dec.skip_win32
+    def test_install_symlink(self):
+        with TemporaryDirectory() as d:
+            f = u'ƒ.js'
+            src = pjoin(d, f)
+            touch(src)
+            install_labextension(src, symlink=True)
+        dest = pjoin(self.system_labext, f)
+        assert os.path.islink(dest)
+        link = os.readlink(dest)
+        self.assertEqual(link, src)
+    
+    @dec.skip_win32
+    def test_overwrite_broken_symlink(self):
+        with TemporaryDirectory() as d:
+            f = u'ƒ.js'
+            f2 = u'ƒ2.js'
+            src = pjoin(d, f)
+            src2 = pjoin(d, f2)
+            touch(src)
+            install_labextension(src, symlink=True)
+            os.rename(src, src2)
+            install_labextension(src2, symlink=True, overwrite=True, destination=f)
+        dest = pjoin(self.system_labext, f)
+        assert os.path.islink(dest)
+        link = os.readlink(dest)
+        self.assertEqual(link, src2)
+
+    @dec.skip_win32
+    def test_install_symlink_destination(self):
+        with TemporaryDirectory() as d:
+            f = u'ƒ.js'
+            flink = u'ƒlink.js'
+            src = pjoin(d, f)
+            touch(src)
+            install_labextension(src, symlink=True, destination=flink)
+        dest = pjoin(self.system_labext, flink)
+        assert os.path.islink(dest)
+        link = os.readlink(dest)
+        self.assertEqual(link, src)
+
+    def test_install_symlink_bad(self):
+        with self.assertRaises(ValueError):
+            install_labextension("http://example.com/foo.js", symlink=True)
+        
+        with TemporaryDirectory() as d:
+            zf = u'ƒ.zip'
+            zsrc = pjoin(d, zf)
+            with zipfile.ZipFile(zsrc, 'w') as z:
+                z.writestr("a.js", b"b();")
+        
+            with self.assertRaises(ValueError):
+                install_labextension(zsrc, symlink=True)
+
+    def test_install_destination_bad(self):
+        with TemporaryDirectory() as d:
+            zf = u'ƒ.zip'
+            zsrc = pjoin(d, zf)
+            with zipfile.ZipFile(zsrc, 'w') as z:
+                z.writestr("a.js", b"b();")
+        
+            with self.assertRaises(ValueError):
+                install_labextension(zsrc, destination='foo')
+
+    def test_labextension_enable(self):
+        with TemporaryDirectory() as d:
+            f = u'ƒ.js'
+            src = pjoin(d, f)
+            touch(src)
+            install_labextension(src, user=True)
+            enable_labextension(section='notebook', require=u'ƒ')
+        
+        config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
+        cm = BaseJSONConfigManager(config_dir=config_dir)
+        enabled = cm.get('notebook').get('load_extensions', {}).get(u'ƒ', False)
+        assert enabled
+    
+    def test_labextension_disable(self):
+        self.test_labextension_enable()
+        disable_labextension(section='notebook', require=u'ƒ')
+        
+        config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
+        cm = BaseJSONConfigManager(config_dir=config_dir)
+        enabled = cm.get('notebook').get('load_extensions', {}).get(u'ƒ', False)
+        assert not enabled
+        
+
+    def _mock_extension_spec_meta(self, section='notebook'):
+        return {
+            'name': 'mockextension',
+            'src': 'mockextension',
+        }
+
+    def _inject_mock_extension(self, section='notebook'):
+        outer_file = __file__
+
+        meta = self._mock_extension_spec_meta(section)
+
+        class mock():
+            __file__ = outer_file
+            
+            @staticmethod
+            def _jupyter_labextension_paths():
+                return [meta]
+        
+        import sys
+        sys.modules['mockextension'] = mock
+        
+    def test_labextensionpy_files(self):
+        self._inject_mock_extension()
+        install_labextension_python('mockextension')
+        
+        assert check_labextension('_mockdestination/index.js')
+        assert check_labextension(['_mockdestination/index.js'])
+        
+    def test_labextensionpy_user_files(self):
+        self._inject_mock_extension()
+        install_labextension_python('mockextension', user=True)
+        
+        assert check_labextension('_mockdestination/index.js', user=True)
+        assert check_labextension(['_mockdestination/index.js'], user=True)
+        
+    def test_labextensionpy_uninstall_files(self):
+        self._inject_mock_extension()
+        install_labextension_python('mockextension', user=True)
+        uninstall_labextension_python('mockextension', user=True)
+        
+        assert not check_labextension('_mockdestination/index.js')
+        assert not check_labextension(['_mockdestination/index.js'])
+        
+    def test_labextensionpy_enable(self):
+        self._inject_mock_extension('notebook')
+        install_labextension_python('mockextension', user=True)
+        enable_labextension_python('mockextension')
+        
+        config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
+        cm = BaseJSONConfigManager(config_dir=config_dir)
+        enabled = cm.get('notebook').get('load_extensions', {}).get('_mockdestination/index', False)
+        assert enabled
+        
+    def test_labextensionpy_disable(self):
+        self._inject_mock_extension('notebook')
+        install_labextension_python('mockextension', user=True)
+        enable_labextension_python('mockextension')
+        disable_labextension_python('mockextension', user=True)
+        
+        config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
+        cm = BaseJSONConfigManager(config_dir=config_dir)
+        enabled = cm.get('notebook').get('load_extensions', {}).get('_mockdestination/index', False)
+        assert not enabled
+
+    def test_labextensionpy_validate(self):
+        self._inject_mock_extension('notebook')
+
+        paths = install_labextension_python('mockextension', user=True)
+        enable_labextension_python('mockextension')
+
+        meta = self._mock_extension_spec_meta()
+        warnings = validate_labextension_python(meta, paths[0])
+        self.assertEqual([], warnings, warnings)
+
+    def test_labextensionpy_validate_bad(self):
+        # Break the metadata (correct file will still be copied)
+        self._inject_mock_extension('notebook')
+
+        paths = install_labextension_python('mockextension', user=True)
+
+        enable_labextension_python('mockextension')
+
+        meta = self._mock_extension_spec_meta()
+        meta.update(require="bad-require")
+
+        warnings = validate_labextension_python(meta, paths[0])
+        self.assertNotEqual([], warnings, warnings)
+
+    def test_labextension_validate(self):
+        # Break the metadata (correct file will still be copied)
+        self._inject_mock_extension('notebook')
+
+        install_labextension_python('mockextension', user=True)
+        enable_labextension_python('mockextension')
+
+        warnings = validate_labextension("_mockdestination/index")
+        self.assertEqual([], warnings, warnings)
+
+    def test_labextension_validate_bad(self):
+        warnings = validate_labextension("this-doesn't-exist")
+        self.assertNotEqual([], warnings, warnings)
+