Error in non-jQuery initialization with one option

Error in non-jQuery initialization with one option

takeshisatotakeshisato Posts: 8Questions: 2Answers: 0

Hi,
I've had success with the simplest form of Non-jQuery initialization below.

import DataTable from "datatables.net-bs5";

export default function initTable() {
    let table = new DataTable('#example');
    return table;
}

Then following the example in Non-jQuery Options,
I added one option "{ paging: false }" to the code in the successful simple initialization of DataTable.

import DataTable from "datatables.net-bs5";

export default function initTable() {
    let table = new DataTable('#example', { paging: false });
    return table;
}

This code gave an error below in jest's test.
And this code is for this attempt, published at https://github.com/SatoTake2019/datatables-webpack

I would appreciate any advice on how to make this successful.
Thank you in advance.

  console.error
    Error: Uncaught [TypeError: Cannot read properties of undefined (reading 'dataTable')]        
        at reportException (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jsdom\lib\jsdom\living\helpers\runtime-script-errors.js:66:24)
        at innerInvokeEventListeners (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:353:9)
        at invokeEventListeners (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:286:3)
        at DocumentImpl._dispatch (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:233:9)
        at DocumentImpl.dispatchEvent (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:104:17)
        at Document.dispatchEvent (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jsdom\lib\jsdom\living\generated\EventTarget.js:241:34)
        at Object.dispatchEvent (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\__tests__\tables.test.js:10:18)
        at Promise.then.completed (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\utils.js:289:28)
        at new Promise (<anonymous>)
        at callAsyncCircusFn (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\utils.js:222:10)
        at _callCircusTest (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:248:40)
        at processTicksAndRejections (node:internal/process/task_queues:95:5)
        at _runTest (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:184:3)
        at _runTestsForDescribeBlock (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:86:9)
        at _runTestsForDescribeBlock (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:81:9)
        at run (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:26:3)
        at runAndTransformResultsToJestFormat (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\legacy-code-todo-rewrite\jestAdapterInit.js:120:21)
        at jestAdapter (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\legacy-code-todo-rewrite\jestAdapter.js:79:19)
        at runTestInternal (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-runner\build\runTest.js:367:16)
        at runTest (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-runner\build\runTest.js:444:34) {
      detail: TypeError: Cannot read properties of undefined (reading 'dataTable')
          at new Object.<anonymous>.module.exports (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\datatables.net-bs5\js\dataTables.bootstrap5.js:27:16)        
          at initTable (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\initTable.js:4:17)
          at Document.<anonymous> (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\__tests__\tables.test.js:6:34)

          ....................................(omitted several lines due to character limit).........................................

          at DocumentImpl._dispatch (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:233:9)
          at DocumentImpl.dispatchEvent (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:104:17)
          at Document.dispatchEvent (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jsdom\lib\jsdom\living\generated\EventTarget.js:241:34)
          at Object.dispatchEvent (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\__tests__\tables.test.js:10:18)
          at Promise.then.completed (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\utils.js:289:28)
          at new Promise (<anonymous>)
          at callAsyncCircusFn (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\utils.js:222:10)
          at _callCircusTest (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:248:40)
          at processTicksAndRejections (node:internal/process/task_queues:95:5)
          at _runTest (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:184:3)
          at _runTestsForDescribeBlock (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:86:9)
          at _runTestsForDescribeBlock (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:81:9)
          at run (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\run.js:26:3)
          at runAndTransformResultsToJestFormat (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\legacy-code-todo-rewrite\jestAdapterInit.js:120:21)
          at jestAdapter (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-circus\build\legacy-code-todo-rewrite\jestAdapter.js:79:19)
          at runTestInternal (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-runner\build\runTest.js:367:16)
          at runTest (D:\PyCharmProjects\datatables-webpack\myapps\static\js\__datatables__\node_modules\jest-runner\build\runTest.js:444:34),
      type: 'unhandled exception'
    }

       8 |
       9 |         const event = new Event('DOMContentLoaded');
    > 10 |         document.dispatchEvent(event);
         |                  ^
      11 |     });
      12 | });

      at VirtualConsole.<anonymous> (node_modules/jest-environment-jsdom/build/index.js:60:23)    
      at reportException (node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:70:28)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:353:9)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3)
      at DocumentImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9)
      at DocumentImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17)
      at Document.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34)
      at Object.dispatchEvent (__tests__/tables.test.js:10:18)

 FAIL  __tests__/tables.test.js
  test Datatable initialization
    × init test (42 ms)

  ● test Datatable initialization › init test

    TypeError: Cannot read properties of undefined (reading 'dataTable')

      2 |
      3 | export default function initTable() {
    > 4 |     let table = new DataTable('#example', { paging: false });
        |                 ^
      5 |     return table;
      6 | }
      7 |

      at new Object.<anonymous>.module.exports (node_modules/datatables.net-bs5/js/dataTables.bootstrap5.js:27:16)
      at initTable (initTable.js:4:17)
      at Document.<anonymous> (__tests__/tables.test.js:6:34)
      at Document.callTheUserObjectsOperation (node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3)
      at DocumentImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9)
      at DocumentImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17)
      at Document.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34)
      at Object.dispatchEvent (__tests__/tables.test.js:10:18)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total

that's all

This question has accepted answers - jump to:

Answers

  • allanallan Posts: 63,514Questions: 1Answers: 10,472 Site admin

    Hi,

    Thanks for the link to the repo. I'm afraid you are going to have to give me instructions on how I can reproduce the error though / build the code in the repo. I don't see a package.json file for example?

    Allan

  • takeshisatotakeshisato Posts: 8Questions: 2Answers: 0

    Hi allan,

    Sorry for my lack of information.
    The JavaScript base directory is https://github.com/SatoTake2019/datatables-webpack/tree/jest-err-with-one-option/myapps/static/js/__datatables__/. Deep down :)

    So when you open this repo in an IDE like Microsoft's VSCode, open the "__datatables__" folder. There is package.json in that directory, so if you have installed yarn globally, "yarn test" will run Jest tests.

    takeshi

  • takeshisatotakeshisato Posts: 8Questions: 2Answers: 0

    ... and I found out one more thing…

    I took the next step with the above code which did not pass Jest's test.
    When I successfully built the above code with webpack and started Django's runserver, bundle.js no longer gave me errors in the browser.

    This means that the code built with webpack is usable, but it also requires unit tests to be successful.

    Can you tell me how to get it to pass Jest tests?

  • allanallan Posts: 63,514Questions: 1Answers: 10,472 Site admin

    Thanks for the directions. I've just tried:

    $ npm run test
    
    > datatables-webpack-trying@1.0.0 test
    > jest --no-cache --runInBand --watchALL=false
    
     PASS  __tests__/tables.test.js
      test Datatable initialization
        ✓ init test (5 ms)
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        0.834 s
    Ran all test suites.
    

    I'm not sure what I'm missing?

    Allan

  • takeshisatotakeshisato Posts: 8Questions: 2Answers: 0

    Allan, thank you for trying.

    However, the git branch you tried seems to be the "trying" branch, not the "jest-err-with-one-option" branch that causes Jest to error. The branch that causes the error has the option {paging: false} in the initialization expression.

    In this repository, success and error cases are distinguished by placing them in their respective git branches.
    I think that the explanation is not enough for other people to reproduce the error, so I am going to rewrite part of it, including how to start Django's runserver.
    Please wait a few days.

    Takeshi

  • takeshisatotakeshisato Posts: 8Questions: 2Answers: 0

    Hello Allan,
    I cleaned up the project above, added some more explanations, and created another repository called datatables-jest-webpack. Please try again with the new repository.
    The old repository posted above will be removed in a few days.

    How to reproduce each case.
    In this repository, success and error cases are differentiated by placing them in their respective git branches. The result of each branch is in the table below.


    branch unit tests with Jest build with webpack run on browser
    ok-init-without-options successful successful successful
    error-init-with-options fail with errors successful successful

    Then if you reproduce the error case.

    $ git checkout error-init-with-options
    $ cd myapps/static/js/__datatables__/
    $ yarn test
    

    These commands result in a jest error.
    Yet a build using the following command succeeds:

     $ yarn dev 
    

    Then run the Django's runserver gives no error.

    (venv)$ pip install -r requirements.txt
    (venv)$ cd myapps 
    (venv)$ python manage.py runserver --settings=config.settings
    

    This means that bundled js code built with webpack is available in production.
    However, I need the Jest unit test to pass at the same time, so please tell me how to do that.

    (README.md in this repository also has detailed instructions on how to reproduce)

    I tried to write the README and this post as carefully as possible, but I'm not good at English, so I hope I can convey it well.

    Takeshi

  • allanallan Posts: 63,514Questions: 1Answers: 10,472 Site admin
    edited January 2023 Answer ✓

    Hi Takeshi,

    Many thanks for documenting this so thoroughly! Unfortunately, I don't have a direct answer to resolve this at the moment, but I do know what is going wrong.

    What is happening is that the Webpack build is loading DataTables using ES modules. Jest however is using the UMD loader (specifically CommonJS). Those two have slightly different export signatures, which is why this error is being encountered.

    I've read over this part of the Jest documentation about using ESM, and this SO post on the topic, but haven't been able to resolve it.

    I can tell that it isn't using the ESM files for DataTables due to this line in the error back trace:

    at new Object.<anonymous>.module.exports (/home/vagrant/xxtmp/datatables-jest-webpack/myapps/static/js/__datatables__/node_modules/datatables.net-bs5/js/dataTables.bootstrap5.js:29:16)

    If it were ESM then dataTables.bootstrap5.mjs would have been the file that was loaded.

    I know this is a pain, but I think this is actually a good thing, since it shows that the environment Jest is running and the one that is used in the browser is not the same - they are using two different files, so the tests aren't actually testing what the site is running!

    The key will be to somehow tell Jest to run the ESModules for DataTables. But how to do that (since I couldn't with the documentation on the Jest site), I'm afraid I just don't know.

    I think you'll need input from someone who is far more experienced with Jest that I am to get it fully resolved (if you do - please post back with that information so I can learn as well!).

    Allan

  • takeshisatotakeshisato Posts: 8Questions: 2Answers: 0

    Hello Allan,

    Thanks for the clarification and helpful link information that Jest needs to load the ESModule for DataTables.
    In fact, when I was testing some variations of the DataTable initialization code yesterday, I found that a Jest's contributor said "We don't support amd, you must use umd" in the Jest's Issue Tracker. And in an article Hands-on Node.js native ESM — Wantedly application migration example (written in Japanese though) I read, it says, “Libraries like Jest and Storyshot, which tweak Node.js's module system or are tightly coupled to them, so adapting to ESM can be difficult.” So It seems that the solution is not easy. But if I have any progress or information regarding this problem, I will put another post separately on this forum to share the information.

    And one more thing, It's not the root solution to the above problem, but I'm trying another workaround.
    Before closing this topic, I would like to ask you to see whether it makes sense from your experience.
    Please give me a few days to test it some more and implement it in another branch of my repository.

    thank you,
    takeshi

  • allanallan Posts: 63,514Questions: 1Answers: 10,472 Site admin
    edited January 2023 Answer ✓

    Hi Takeshi,

    Module loaders are a bit of a nightmare! A UMD (Universal Module Loader) is a wrapper that will allow a piece of code to be used in any of three environments:

    • The browser
    • CommonJS
    • AMD (RequireJS)

    So I think the Jest contributor misspoke slightly there - they support CommonJS, which is one of the loaders supported by a UMD.

    What really messes things up is the current transition that the Javascript world is going through to EM Modules - a forth loader type! Ultimately it will be excellent, but it needs all the libraries to catch up.

    There are three options and I can see here:

    1. Force WebPack to use CommonJS rather than ES modules. This might be quite difficult, it might not, I'm honestly not sure how it would be done.
    2. Use DataTables 1.12.x rather than 1.13+. Our ES Module support was added in 1.13, so before it would force the use of the UMD (and thus CommonJS)
    3. You could add extra code to check if the UMD or ESM was loaded. The CommonJS loader for DataTables has a slightly different signature which is what is causing this issue.

    I haven't tested it, but you might be able to do something like:

    import * as DT from 'datatables.net-bs5';
    
    const DataTable = DT.default
      ? DT.default
      : DT();
    

    Personally I really wouldn't like to do that though, since as I mentioned, it means your test and "real" code are going through two different paths!

    The best way would be to see if Jest can be made to run with the ES module.

    Allan

  • takeshisatotakeshisato Posts: 8Questions: 2Answers: 0

    Hello Allan,

    "your test and "real" code are going through two different paths!"

    Yes, the initialization workaround I was trying was to conditionally separate the two paths. The code is below:

    import DataTable from "datatables.net-bs5";
    import "datatables.net-fixedcolumns-bs5";
    import "datatables.net-fixedheader-bs5";
    import "datatables.net-rowreorder-bs5";
    
    // export is for test
    export function initTable() {
        let table;
        const options = {
            paging: true,    // Pagination
            searching: true,  // Search box
            lengthChange: false,   // Pulldown Menu for num of items displayed per page
            autoWidth: false,    // autofit column width
            info: true,          // "Showing 1 to 57 of 57 extries"
        };
    
        if (globalThis.hasOwnProperty('test')) {
            /*
                This initialization expression passes Jest unit tests,
                but webpack-built bundle file gives an error in the browser.
             */
            let DataTableFunc = new DataTable(null, null);
            table = new DataTableFunc('#example', options);
        }
        else {
            /* 
                This initialization expression will fail in Jest test, 
                but webpack-built bundle file goes well on browser.
             */
            table = new DataTable('#example', options);
        }
        return table;
    }
    
    document.addEventListener('DOMContentLoaded', function () {
        let table = initTable();
    });
    

    I created a new branch workaround-for-jest and put the above code in this branch of the same repository.

    I'm not sure if code like this makes sense, but none of the Jest tests, Webpack builds, or running in the browser gave any errors.
    Also, since the options are written in one place, even if there is a change, it is OK to modify only one place. So I can test my own customizations.

    Regarding the table object returned by the initialization function, the representation of the object is different between Jest and the browser, but it seems to have the same functionality. With table.data(), I could see the data for each item in the table.

    And although I wrote only one test, I checked if the optional function before and after initialization is enabled in the test file static/js/__datatables__/__tests__/index.test.js.

        test("test one module", () => {
            // provide an empty implementation for window.alert
            window.alert = () => {};
    
            document.body.innerHTML = fs.readFileSync(
                __dirname + `/rendered.html`, { encoding: "utf-8" });
    
            // these elements are not existing before init
            let pagination_elements = document.getElementsByClassName('pagination');
            expect(pagination_elements).toEqual(expect.arrayContaining([]));
            let search_elements = document.getElementById('example_filter');
            expect(search_elements).toBeNull();
            let info_element = document.getElementById('example_info');
            expect(info_element).toBeNull();
    
            document.addEventListener('DOMContentLoaded', function () {
                let table = initTable();
            });
    
            const event = new Event('DOMContentLoaded');
            document.dispatchEvent(event);
    
            // if options["paging"] = true
            pagination_elements = document.getElementsByClassName('pagination');
            expect(pagination_elements).toBeDefined();
            // if options['searching'] = true
            search_elements = document.getElementById('example_filter');
            expect(search_elements).toBeDefined();
            // if options['info'] = true
            info_element = document.getElementById('example_info');
            expect(info_element.innerHTML).toMatch(/^Showing.*entries/)
        });
    
  • allanallan Posts: 63,514Questions: 1Answers: 10,472 Site admin

    Thanks for posting your workaround. As you say, a bit nasty, but if it works... :)

    Allan

Sign In or Register to comment.