From 022b0bd680c033f028ee6b54024e78e367488b91 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Thu, 8 Aug 2024 17:39:03 +0200 Subject: [PATCH] Replace the test runner for JS tests PhantomJS is not maintained anymore and the setup was broken. The new setup uses `@web/test-runner` to run the QUnit tests in an actual browser. --- .github/workflows/run-tests.yml | 28 +++++++--- Resources/js/run-qunit.js | 82 ---------------------------- Resources/js/translatorTest.js | 2 + Resources/package.json | 7 ++- Resources/web-test-runner.config.mjs | 23 ++++++++ 5 files changed, 51 insertions(+), 91 deletions(-) delete mode 100644 Resources/js/run-qunit.js create mode 100644 Resources/web-test-runner.config.mjs diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 95de6559..a62ec679 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -15,8 +15,7 @@ env: jobs: test: name: PHP ${{ matrix.php-version }} + Symfony ${{ matrix.symfony-version }} - # TODO find a different setup for the JS testsuite as phantomjs is abandoned and is not available on newer runner images - runs-on: 'ubuntu-20.04' + runs-on: 'ubuntu-latest' continue-on-error: ${{ matrix.allowed-to-fail }} strategy: @@ -44,7 +43,7 @@ jobs: symfony-version: latest steps: - name: "Checkout code" - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v4.1.7 - name: "Install PHP with extensions" uses: shivammathur/setup-php@2.7.0 @@ -77,8 +76,23 @@ jobs: - name: "Run unit tests" run: vendor/bin/phpunit --coverage-text --verbose - - name: "Run phantomjs tests" - run: phantomjs Resources/js/run-qunit.js file://`pwd`/Resources/js/index.html + test_js: + name: JS tests + runs-on: ubuntu-latest - - name: "Run phantomjs tests ES5" - run: phantomjs Resources/js/run-qunit.js file://`pwd`/Resources/js/index-with-es5-shim.html + steps: + - name: "Checkout code" + uses: actions/checkout@v4.1.7 + + - name: "Setup node" + uses: actions/setup-node@v4.0.3 + with: + node-version: 'lts/*' + + - name: "Install dependencies" + run: npm install + working-directory: Resources + + - name: "Run tests" + run: npm test + working-directory: Resources diff --git a/Resources/js/run-qunit.js b/Resources/js/run-qunit.js deleted file mode 100644 index 1411d90d..00000000 --- a/Resources/js/run-qunit.js +++ /dev/null @@ -1,82 +0,0 @@ -var system = require('system'); - -/** - * Wait until the test condition is true or a timeout occurs. Useful for waiting - * on a server response or for a ui change (fadeIn, etc.) to occur. - * - * @param testFx javascript condition that evaluates to a boolean, - * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or - * as a callback function. - * @param onReady what to do when testFx condition is fulfilled, - * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or - * as a callback function. - * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. - */ -function waitFor(testFx, onReady, timeOutMillis) { - var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timout is 3s - start = new Date().getTime(), - condition = false, - interval = setInterval(function() { - if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) { - // If not time-out yet and condition not yet fulfilled - condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code - } else { - if(!condition) { - // If condition still not fulfilled (timeout but condition is 'false') - console.log("'waitFor()' timeout"); - phantom.exit(1); - } else { - // Condition fulfilled (timeout and/or condition is 'true') - console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms."); - typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled - clearInterval(interval); //< Stop this interval - } - } - }, 100); //< repeat check every 250ms -} - -if (system.args.length === 1 || system.args.length > 3) { - console.log('Usage: run-qunit.js URL'); - phantom.exit(); -} - -var page = new WebPage(); - -// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") -page.onConsoleMessage = function(msg) { - console.log(msg); -}; - -page.open(system.args[1], function(status){ - if (status !== "success") { - console.log("Unable to access network"); - phantom.exit(); - } else { - waitFor(function(){ - return page.evaluate(function(){ - var el = document.getElementById('qunit-testresult'); - if (el && el.innerText.match('completed')) { - return true; - } - return false; - }); - }, function(){ - var failedNum = page.evaluate(function(){ - var el = document.getElementById('qunit-testresult'); - console.log(el.innerText); - - var fails = document.querySelectorAll('#qunit-tests > .fail > ol > .fail'); - [].forEach.call(fails, function(fail, index) { - console.log("\nFail #" + index + ":") - console.log(fail.innerHTML); - }); - - try { - return el.getElementsByClassName('failed')[0].innerHTML; - } catch (e) { } - return 10000; - }); - phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0); - }); - } -}); diff --git a/Resources/js/translatorTest.js b/Resources/js/translatorTest.js index 032aeed5..9201f53f 100644 --- a/Resources/js/translatorTest.js +++ b/Resources/js/translatorTest.js @@ -242,6 +242,8 @@ QUnit.test('retry on fallback when locale exist but not the domain', function(as QUnit.test('gets the current locale using the `lang` attribute on the `html` tag', function(assert) { assert.expect(1); + document.documentElement.lang = 'fr'; + assert.equal(Translator.locale, 'fr'); }); diff --git a/Resources/package.json b/Resources/package.json index 6254b378..46a27337 100644 --- a/Resources/package.json +++ b/Resources/package.json @@ -23,11 +23,14 @@ ], "main": "js/translator.js", "devDependencies": { + "@web/test-runner": "^0.18.2", "intl-messageformat": "^10.5.14", "qunit": "^2.21.1", - "uglify-js": "^2.7.3" + "uglify-js": "^2.7.3", + "web-test-runner-qunit": "^2.0.0" }, "scripts": { - "build": "uglifyjs js/translator.js --comments --compress --mangle --output=public/js/translator.min.js" + "build": "uglifyjs js/translator.js --comments --compress --mangle --output=public/js/translator.min.js", + "test": "wtr js/translatorTest.js" } } diff --git a/Resources/web-test-runner.config.mjs b/Resources/web-test-runner.config.mjs new file mode 100644 index 00000000..797aefe1 --- /dev/null +++ b/Resources/web-test-runner.config.mjs @@ -0,0 +1,23 @@ +export default { + nodeResolve: true, + // Our QUnit tests expect the translator to be loaded globally so we need to inject it in the runner HTML + testRunnerHtml: testFramework => + ` + + + + + JSTranslationBundle Unit Tests + + + + + + + + `, + testFramework: { + path: './node_modules/web-test-runner-qunit/dist/autorun.js', + config: {} + } +}