Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update QUnit, use Karma, make some of CI work locally as well #672

Merged
merged 3 commits into from
Jan 3, 2021

Conversation

Krinkle
Copy link
Collaborator

@Krinkle Krinkle commented Nov 22, 2020

  • Load qunit package from npm. Start of a larger transition, ref Introduce npm+webpack to build the app #554.

  • Update QUnit from 2.3 to 2.12.

  • Use Karma for the running of unit tests (instead of Nightwatch).

    While it was possible to use a fake "UI" test to run a QUnit web page with Nightwatch, this was not ideal. This had numerous limitations, such as:

    • relies on fragile an unsupported web scraping to collect results, which can easily break between versions.
      ref qunit updated #660.
    • results provide limited debugging information when a test fails.
    • cannot easily be run locally from the command-line since the Nightwatch config is currently written for SauceLabs, and creating a local configuration is not easy because Nightwatch has a hard requirement on webdriver (which makes sense for UI tests), which people usually do not have installed or may be incompatible with the current version of their browser. These then also have to be configured and started etc.
    • cannot easily be run in a secure container separate from your personal computer, thus putting personal data at risk.
    • lacks wider integration and plugins to enrich unit testing, such as test coverage reports.
  • Using Karma means:

    • You can run 'npm test' locally during development and have it automatically run the tests in headless Firefox and Chrome and report back, all from the command-line.
    • The same exact runner is also used in CI with SauceLabs for additional browser coverage (same as before).
    • It has no external dependencies other than the plain web browser itself. This means if you have a development container (e.g. based on Docker) that has Node.js + Firefox + Chromium, you can run the tests from within there without exposing anything from your personal computer, except the current working directory.
      https://timotijhof.net/posts/2019/protect-yourself-from-npm/
    • In a future change, we can plug in karma-coverage to generate a test coverage report, and then submit this to Codecov or Coveralls, ref Introduce code coverage with Codecov #528.
  • I have restructured the Travis CI file so that the cross-browser test in SauceLabs are skipped for PRs from forks. Instead, those PRs will run the unit tests in local Fireox and Chrome within Travis CI. This avoids the confusing failures and still gives (some) useful results.

    The tests that Travis CI runs are the same as what users can run locally if they invoke npm test (after a one-time run of npm install to fetch dependencies into the local node_modules directory).

    This restructuring uses the 'stages' feature which is not compatible with the 'deploy' feature. I had converted the two deploy stages to use the 'stages' feature as well so that these continue to work.

  • I have pinned the version of 'http-server' and 'nightwatch' in package.json so that these don't silently switch major versions that may contain breaking changes.

Fixes #653.

@Krinkle
Copy link
Collaborator Author

Krinkle commented Nov 22, 2020

In my fork, I have pushed a second branch in which I have this commit with the modification to enable the "SauceLabs" stage even for forks, and added my own SauceLabs credentials via Travis CI. This to confirm that this stage also works as intended:

https://travis-ci.com/github/Krinkle/kiwix-js/builds/203361391

@kelson42 kelson42 requested review from Jaifroid and mossroy November 22, 2020 06:30
.travis.yml Outdated Show resolved Hide resolved
package.json Show resolved Hide resolved
Copy link
Member

@Jaifroid Jaifroid left a comment

Choose a reason for hiding this comment

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

@Krinkle This looks excellent to me. I haven't done any testing. Do we need central credentials for Karma? My only other comment, as you will have noticed, is to add edge87 or edge86 if available on the testing platform.

Copy link
Contributor

@mossroy mossroy left a comment

Choose a reason for hiding this comment

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

Many thanks for this PR!
Thanks for the improvement on Travis build on forks : having unit tests is always better than no CI at all

I thought that Karma would replace nightwatch even for UI tests, but I probably misunderstood. The code coverage on current unit tests alone would not be very relevant

.travis.yml Outdated Show resolved Hide resolved
@@ -57,7 +57,7 @@ The source code can be found at https://github.com/kiwix/kiwix-js

## Unit tests

Unit tests can be run by opening tests.html file on Firefox or Edge (or Chromium/Chrome with some tweaks).
Unit tests can be run by opening `tests/index.html` file in Firefox, Edge, or Chromium/Chrome.
Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose this is not true any more : unit tests need qunit from node_modules directory, and they still need to go through a local webserver.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The way I've run these ad-hoc is by executing php -S localhost:4000 or python3 -m http.server 4000 to start a static server from the repository directory. These are (while using somewhat unrelated technologies) are rather ubiquitous, simple, and dependency-free.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I removed "some tweaks" since I encountered no issue running these in Chrome. I'm not sure what these tweaks referred to, but I guess there was an issue at some point that required modifying some of the files?

I'm happy to revert this change. Did I misunderstand?

Copy link
Member

Choose a reason for hiding this comment

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

I think the tweaks referred to running the file from file:// (--allowfileaccessfromfile or something like that), but this has largely been blocked by all but legacy browsers. So it's fine to remove the reference to tweaks.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree to remove the tweaks reference, but we should mention here what is necessary to do for a newcomer : if a npm install is necessary or not, and that it must be opened through a local webserver (and suggest the php and python ways)

Copy link
Contributor

Choose a reason for hiding this comment

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

If npm install is now necessary, it also should be mentioned at the beginning of the body of tests/index.html

package.json Show resolved Hide resolved
.travis.yml Outdated Show resolved Hide resolved
tests/karma.conf.saucelabs.js Show resolved Hide resolved
tests/karma.conf.saucelabs.js Show resolved Hide resolved
.travis.yml Outdated Show resolved Hide resolved
@mossroy
Copy link
Contributor

mossroy commented Nov 22, 2020

Regarding the code coverage, we should probably add more unit tests.
In my paid job, I advocate to have a strong unit test coverage (that run quickly and reliably, on the developer workstation), and rely less on E2E tests : it might be time to apply that on kiwix-js :-)
But it's probably not easy, and might need to refactor our monster app.js to make it more testable

tests/index.html Outdated Show resolved Hide resolved
@Krinkle
Copy link
Collaborator Author

Krinkle commented Nov 22, 2020

If I understood correctly, Karma is used for unit tests only (with QUnit), and nightwatch is kept for UI tests?
Do you think it would be possible to use Karma for both?
Else the karma code coverage would not be very relevant.

In my understanding, it is usually not wanted for end-to-end UI tests to have code coverage tracked. Are you sure you want that? Or could perhaps some of the UI tests be better written as an integration test within QUnit? It depends on how modular the code is, e.g. if part of it can be instantiated at will and then torn down again.

For end-to-end UI tests this would be quite hard. They tend to need multiple concurrent browser sessions, which would need to be aggregated, and capturing at the end of each session will not work if the test involved a form submission or other navigation or reload. Code coverage generally works by transforming every branch of every statement in the code with a wrapper that tracks the offset and original file name in a large tree stored under a global variable (e.g. Istanbul, NYC, V8, etc.) - which would be lost upon browser navigation.

Having said that, for an SPA where there are no "real" browser navigations - this might work. But, I get the impression that Kiwix is moving toward PWAs for a more stable user experience, lower memory use, faster performance, etc.

I've looked into Nightwatch.js and WebdriverIO, as popular webdriver frameworks, and from what I can tell they don't really have a good way to do this. But.. it's not impossible. The lower-level libraries for Instanbul code instrumentation could be used directly in a custom script for Kiwix, and then we'd need a version of the www app that serves those files instead, and then before every click and navigation we'd somehow serialize and send this to a (separate) Node.js local server, combine each of the files, and then write it to NYC to create the coverage summary reports.

But the way I would normally do this elsewhere is for most tests to be to use QUnit for both unit tests (no shared state, observe one class or function at a time, inject/construct any needed state) and integration tests (some shares state, drive the main application). And then use end-to-end test with webdriver to validate a small number of scenarios for each high-level feature to confirm that the (nicely separate) UI code works correctly, which would be excluded from the code coverage percentage.

And it also duplicates target browsers definitions

Acknowedged, this is a small cost to make the most of UI tests and unit tests in their own right. Unfortunately, while they are similar, the needs for each are different enough that the industry has generally solved them in different ways.

For UI tests, it is expected that a web application and server already exists and are launched in their "normal" way, no modifications or special instrumentations. And we launch the browser through a remote control "webdriver", which requires extra software or bridges to be installed (geckdriver, chromedriver, etc.). Capturing code coverage from here is hard. But, you can navigate around a site from a single test case and perform UI actions in a realistic way. E.g. typing into text fields is simulated the way a user would do it.

For unit tests, there is no web app or server at play. They run either in Node.js itself, or in an empty browser window. Source code is loaded, and then the program executes in a non-visual way. Classes are instantiates, functions are called etc. At the end of it all, coverage is collected from the (one, empty) browser window in which all tests ran, and you have a coverage report. All code executes client-side. There is no webdriver or remote control involved. These tests run generally much faster as there are no reloads and no intermediary remote controls, you could afford to have hundreds of variations of a specific interaction to ensure all edge cases are covered without it taking very long.

@Krinkle
Copy link
Collaborator Author

Krinkle commented Nov 22, 2020

[…] Do we need central credentials for Karma?

No, this continues to use the same SauceLabs credentials, and will read them from the same secure env variables as before.

@mossroy
Copy link
Contributor

mossroy commented Nov 23, 2020

Thanks for your feedback on tests @Krinkle
It seems to correspond to the thoughts I had afterwards in #672 (comment)
I agree with you that trying to measure code coverage on UI tests is a bad idea.
What we should do is measure the code coverage on karma (unit tests) : this coverage will be disappointing, which should push us to do more unit tests.

Regarding the browsers configuration duplication, it's not a big issue to keep it as it does not evolve frequently : it should be enough to put a comment in both places to remind to keep them in sync

@Krinkle
Copy link
Collaborator Author

Krinkle commented Nov 23, 2020

Thanks, I'll rebase and amend for the above points next weekend.

Copy link
Contributor

@mossroy mossroy left a comment

Choose a reason for hiding this comment

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

Apart from my latest minor comments, it looks good to me.
Merging this would allow to test the CI in real conditions

@Krinkle
Copy link
Collaborator Author

Krinkle commented Nov 29, 2020

@mossroy I thought I had addressed them all. Can you remind me which ones I missed?

@mossroy
Copy link
Contributor

mossroy commented Nov 29, 2020

@Krinkle
Copy link
Collaborator Author

Krinkle commented Nov 29, 2020

Oops, I hadn't seen those indeed. Will do!

@Jaifroid Jaifroid removed this from the v3.1 milestone Dec 6, 2020
@kelson42
Copy link
Collaborator

Branch needs to be rebased as well.

@Jaifroid
Copy link
Member

Jaifroid commented Dec 13, 2020

@kelson42 I think the only conflict here is the .travis.yml file, from which we have removed the Continuous Integration code, since it is now handled by GitHub Actions. Since Travis has stopped working entirely for us due to their decision to start charging Open Source projects (and we have no credit left), we need to re-evaluate with @Krinkle which parts of this PR can be transferred to GitHub Actions. The underlying changes for QUnit are still extremely useful for us.

@Krinkle
Copy link
Collaborator Author

Krinkle commented Dec 21, 2020

@Jaifroid Do I understand correctly that the unit tests have been disabled currently? I don't find the analogous code in the .github/ directory currently to re-apply this change to.

I can help with that transition as well, but someone will need to create the necessary "Secrets" behind this repository for Sauce Labs.

Also, make sure action workflows are enabled for fork PRs, but with secrets disabled. See blog post for more info.

@Jaifroid
Copy link
Member

@Krinkle Yes, unfortunately Travis is completely blocked due to a negative credit balance (caused by a policy change on the Travis side). I have not attempted to write equivalent code to launch the tests on GitHub Actions because I am unfamiliar with the operation of Unit Tests and I'm aware of potentially conflicting with this PR. I did manage, with @mossroy's help, to write CI.yml which launches build scripts for nightly and release builds.

If you are able to help with the translation for tests on pull requests and commits, that would be fantastic. @mossroy or I can help with secrets.

@Jaifroid
Copy link
Member

@mossroy I have a login/pw for Sauce labs, but not a private key secret. Do you have that?

@mossroy
Copy link
Contributor

mossroy commented Dec 21, 2020

I have a Sauce username and "Access Key", that I can add as Github Actions secrets

This repo is public so, based on the github blog, we might be able to use pull_request_target to let the CI run on a fork PR

@mossroy
Copy link
Contributor

mossroy commented Dec 21, 2020

I've just added SAUCE_USERNAME and SAUCE_ACCESS_KEY secrets (same names as the corresponding Travis-CI secrets, and normally same values)

Rename this to make space for a separate "CI" workflow that will
run continuous integration and testing jobs for commits and PRs.
* Load qunit package from npm, this is the start of a larger transition.
  ref at kiwix#554.

* Update QUnit from 2.3 to 2.13.

* Use Karma for the running of unit tests (instead of Nightwatch).

  While it was possible to use a fake "UI" test to open the QUnit
  web page with Nightwatch, this had numerous limitations:

  - relies on fragile and unsupported DOM scraping to collect
    test results, which breaks between framework versions.
    ref kiwix#660.

  - severely limits debugging information for failing tests.

  - cannot easily be reproduced or debugged locally from the command-line
    as the Nightwatch config was pinned to Sauce Labs, and creating
    a local configuration is not easy because Nightwatch has a hard
    requirement for installing and running a WebDriver server.
    People usually do not have this installed and it's non-trivial
    to set up and keep working in the long term, and across multiple
    different software projects.

  - cannot easily be run in a secure container separate from your
    personal computer, thus putting personal data at risk.

  - lacks wider integration and plugins to enrich unit testing,
    such as test coverage reports.

  Using Karma means:
  - We can run 'npm test' locally during development and have it
    automatically run the tests in headless Firefox and Chrome
    and report back, all from the command-line.
  - The same exact same stack is also used in CI with SauceLabs
    for additional browser coverage (same as before).
  - It has no external dependencies other than the plain web
    browser itself. This means if you have a development container
    (e.g. based on Docker) that has Node.js + Firefox + Chromium,
    you can run the tests there without exposing anything from
    your personal computer, besides the current directory.
    <https://timotijhof.net/posts/2019/protect-yourself-from-npm/>
  - In a future change, we can plug in karma-coverage to generate
    a test coverage report, to submit to Codecov or Coveralls.
    ref kiwix#528.

* I have pinned the version of 'http-server' and 'nightwatch'
  in package.json so that these don't silently upgrade in a way
  that may introduce security issues or drop compatibility for
  the environment we currently support.

Fixes kiwix#653.
The tests were disabled after kiwix#499
due to an issue with the Edge version that was the default "edge" on
SauceLabs in May 2019 (not sure which version that was, the last pre-Chromium
Edge version was 44, which was passing, so perhaps SauceLabs defaulted
to a beta release, or used a much older version like 15-18?)

Now that Edge uses Chromium, try re-enabling the tests.

Fixes kiwix#502.
@Krinkle
Copy link
Collaborator Author

Krinkle commented Dec 30, 2020

@mossroy Thanks, I think I've got it all figured out now. A ran into a lot of issues, which I've tried to document as best I can.

In particular:

  • On Travis, when creating a pull request secrets are naturally left out and the secure job can be skipped as being a fork-PR. When you run Travis jobs on commits and branches directly in your forked repo (not a PR), you have control and can provide your own secrets, which makes forking and testing easy for contributors. GitHub, workflows on a forked repo are always executed from the upstream perspective even if it is just a push in a local branch and not a PR. This seems like a bug to me, and is made worse by the following:
  • GitHub does let you configure secrets in a forked repo, but this is a dead-end since they are completely ignored. No warning or error.
  • GitHub runs workflow jobs that use (unavailable) secrets. No warning, no error, no easy way to skip based on "is fork" or "has secrets". (I found a workaround.)
  • WebDriver and SauceLabs happy start the test with empty string as username and access key and then timeout on random internal errors. No warning or error. Ref Uncaught TypeError: Cannot read property 'deleteSession' of null karma-runner/karma-sauce-launcher#236. This is where my failure started and it took a lot of digging for me to realize the previous points, which is that the secrets I had configured where in fact completely ignored and replaced by empty strings.
  • Nightwatch and GitHub don't have built-in support for starting a SauceLabs connect tunnel, and WebDriver/SauceLabs don't warn against this, which resulted in additional confusing timeouts. I'd expect some kind of warning for "invalid tunnel-identifier" or "unknown host / tunnel needed" but no such thing. While the Karma/QUnit setup starts the tunnel automatically, for the End-to-end tests we start it in a separate step now. (Ref docs: Encourage use of v1 instead of master saucelabs/sauce-connect-action#22).

In conclusion:

  • On forked pull requests (like this one) we run only the basic tests, the same as I had prepared on Travis previously.
  • On pushes and local pull requests, we also run UI tests and unit tests via SauceLabs. These are gracefully skipped on PRs like this one. To verify that the secure tests for regular pushes pass as well, see https://github.com/kiwix/kiwix-js/actions/runs/451912893.

Copy link
Contributor

@mossroy mossroy left a comment

Choose a reason for hiding this comment

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

Excellent!
Many thanks @Krinkle
One minor thing is about the wording of tests-basic and tests-secure : I fully understand the technical reasons to separate them like that, but it might be confusing names when one does not know this context. On might wonder : "are some CI tests secure, and some other ones not secure?"

@Jaifroid
Copy link
Member

Jaifroid commented Jan 1, 2021

@Krinkle Looks excellent to me too. Thank you so much!

I noticed one unresolved conversation in the code: see here. Not sure if the comment is still relevant.

@mossroy
Copy link
Contributor

mossroy commented Jan 3, 2021

As the remaining remarks are very minor, I propose to squash and merge this PR, if you agree @Jaifroid
So that to re-enable the unit and UI tests for other PRs

@Jaifroid
Copy link
Member

Jaifroid commented Jan 3, 2021

@mossroy Yes of course!

@mossroy mossroy merged commit 8b3a5c3 into kiwix:master Jan 3, 2021
@Jaifroid
Copy link
Member

Jaifroid commented Jan 4, 2021

@Krinkle Thank you very much for this PR. A flag is appearing at the top of our Repo about a security vulnerability in a Node dependency, leading to this report:

image

I guess it's not a big deal (DoS attack), but any way you know of to update the dependency?

@Jaifroid
Copy link
Member

Jaifroid commented Jan 4, 2021

Sorry for further quesiton @Krinkle, but after running npm install, then npm run test-unit-local, I see the following on my local machine (Windows 10):

image

Should Chrome headless be able to run this way? It doesn't seem to work for me. Do I need to take any extra steps?

@mossroy
Copy link
Contributor

mossroy commented Jan 4, 2021

Regarding the ecstatic vulnerability, the right way to do that would be to upgrade http-server to a version that depends on a non-vulnerable version of ecstatic. This can easily be done in package.json file.
Unfortunately, version 0.12.3 is the latest one. The problem has been reported one month ago, but got no answer so far : http-party/http-server#666
It's probably not a real security issue in our context, so I'd suggest to wait for an upgrade of http-server to be available.

@mossroy
Copy link
Contributor

mossroy commented Jan 4, 2021

Regarding the local execution of unit tests with npm, it works on my ubuntu 20.04 with Chromium (with CHROME_BIN=/usr/bin/chromium-browser npm run test-unit-local command line)
Maybe try to set CHROME_BIN environment variable to your local executable binary of Chrome (or Chromium, or maybe the new Edge)

Krinkle added a commit that referenced this pull request Jan 5, 2021
Krinkle added a commit that referenced this pull request Jan 5, 2021
Krinkle added a commit that referenced this pull request Jan 5, 2021
By default `npm test` will run concurrenty in Firefox and one of
Chromium/Chrome. Chrome for macOS and Win, and Chromium on Linux
unless CHROME_BIN is configured by the user's env variables, in
which case they're likely to prefer that and/or not have Chromium
installed.

Also provide `npm run test-unit-…` commands which run unit tests
in a single specific browser only (overrides the conf array).

Follows-up #672.
@Krinkle Krinkle deleted the ci-refactor branch January 5, 2021 00:43
Krinkle added a commit that referenced this pull request Jan 27, 2021
Krinkle added a commit that referenced this pull request Jan 27, 2021
By default `npm test` will run concurrenty in Firefox and one of
Chromium/Chrome. Chrome for macOS and Win, and Chromium on Linux
unless CHROME_BIN is configured by the user's env variables, in
which case they're likely to prefer that and/or not have Chromium
installed.

Also provide `npm run test-unit-…` commands which run unit tests
in a single specific browser only (overrides the conf array).

Follows-up #672.
Krinkle added a commit that referenced this pull request Jan 27, 2021
Krinkle added a commit that referenced this pull request Jan 27, 2021
By default `npm test` will run concurrenty in Firefox and one of
Chromium/Chrome. Chrome for macOS and Win, and Chromium on Linux
unless CHROME_BIN is configured by the user's env variables, in
which case they're likely to prefer that and/or not have Chromium
installed.

Also provide `npm run test-unit-…` commands which run unit tests
in a single specific browser only (overrides the conf array).

Follows-up #672.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update QUnit to latest
4 participants