diff --git a/lib/tasks/smoke_test.rake b/lib/tasks/smoke_test.rake index cb3974cba9d..e5e4473c8c3 100644 --- a/lib/tasks/smoke_test.rake +++ b/lib/tasks/smoke_test.rake @@ -31,7 +31,15 @@ task "smoke:test" do end results = "" - IO.popen("#{phantom_path} #{Rails.root}/spec/phantom_js/smoke_test.js #{url}").each do |line| + + command = + if ENV["USE_CHROME"] + "node #{Rails.root}/test/smoke_test.js #{url}" + else + "#{phantom_path} #{Rails.root}/spec/phantom_js/smoke_test.js #{url}" + end + + IO.popen(command).each do |line| puts line results << line end diff --git a/package.json b/package.json index 524c4c8291f..cb69e4f7bc0 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "dependencies": {}, "devDependencies": { "chrome-launcher": "^0.10.0", - "chrome-remote-interface": "^0.25.4" + "chrome-remote-interface": "^0.25.4", + "puppeteer": "^0.13.0" } } diff --git a/test/smoke_test.js b/test/smoke_test.js new file mode 100644 index 00000000000..e29f4c7076c --- /dev/null +++ b/test/smoke_test.js @@ -0,0 +1,235 @@ +const args = process.argv.slice(2); + +if (args.length < 1 || args.length > 2) { + console.log("Expecting: node {smoke_test.js} {url}"); + process.exit(1); +} + +const url = args[0]; + +console.log(`Starting Discourse Smoke Test for ${url}`); + +const puppeteer = require('puppeteer'); +const path = require('path'); + +(async () => { + const browser = await puppeteer.launch({ + // headless: false, + // slowMo: 10, + args: ["--disable-local-storage"] + }); + const page = await browser.newPage(); + + page.setViewport = { + width: 1366, + height: 768 + }; + + const exec = (description, fn, assertion) => { + const start = +new Date(); + + return fn.call().then(output => { + if (assertion) { + if (assertion.call(this, output)) { + console.log(`PASSED: ${description} - ${(+new Date()) - start}ms`); + } else { + console.log(`FAILED: ${description} - ${(+new Date()) - start}ms`); + console.log("SMOKE TEST FAILED"); + process.exit(1); + } + } else { + console.log(`PASSED: ${description} - ${(+new Date()) - start}ms`); + } + }).catch(error => { + console.log(`ERROR (${description}): ${error.message} - ${(+new Date()) - start}ms`); + console.log("SMOKE TEST FAILED"); + process.exit(1); + }); + }; + + const assert = (description, fn, assertion) => { + return exec(description, fn, assertion); + }; + + page.on('console', msg => console.log(`PAGE LOG: ${msg.text}`)); + + await page.goto(url); + + await exec("expect a log in button in the header", () => { + return page.waitForSelector("header .login-button", { timeout: 500, visible: true }); + }); + + await exec("go to latest page", () => { + return page.goto(path.join(url, 'latest'), { timeout: 1500 }); + }); + + await exec("at least one topic shows up", () => { + return page.waitForSelector(".topic-list tbody tr", { timeout: 500, visible: true }); + }); + + await exec("go to categories page", () => { + return page.goto(path.join(url, 'categories'), { timeout: 1500 }); + }); + + await exec("can see categories on the page", () => { + return page.waitForSelector(".category-list", { timeout: 500, visible: true }); + }); + + await exec("navigate to 1st topic", () => { + return page.click(".main-link a.title:first-of-type", { timeout: 500 }); + }); + + await exec("at least one post body", () => { + return page.waitForSelector(".topic-post", { timeout: 500, visible: true }); + }); + + await exec("click on the 1st user", () => { + return page.click(".topic-meta-data a:first-of-type", { timeout: 500 }); + }); + + await exec("user has details", () => { + return page.waitForSelector("#user-card .names", { timeout: 500, visible: true }); + }); + + if (!process.env.READONLY_TESTS) { + await exec("open login modal", () => { + return page.click(".login-button", { timeout: 500 }); + }); + + await exec("login modal is open", () => { + return page.waitForSelector(".login-modal", { timeout: 500, visible: true }); + }); + + await exec("type in credentials & log in", () => { + let promise = page.type("#login-account-name", process.env.DISCOURSE_USERNAME || 'smoke_user'); + + promise = promise.then(() => { + return page.type("#login-account-password", process.env.DISCOURSE_PASSWORD || 'P4ssw0rd'); + }); + + promise = promise.then(() => { + return page.click(".login-modal .btn-primary", { timeout: 500 }); + }); + + return promise; + }); + + await exec("is logged in", () => { + return page.waitForSelector(".current-user", { timeout: 2000, visible: true }); + }); + + await exec("go home", () => { + return page.click("#site-logo, #site-text-logo", { timeout: 500 }); + }); + + await exec("it shows a topic list", () => { + return page.waitForSelector(".topic-list", { timeout: 1500, visible: true }); + }); + + await exec("we have a create topic button", () => { + return page.waitForSelector("#create-topic", { timeout: 500, visible: true }); + }); + + await exec("open composer", () => { + return page.click("#create-topic", { timeout: 500 }); + }); + + await exec("the editor is visible", () => { + return page.waitForFunction( + "document.activeElement === document.getElementById('reply-title')", + { timeout: 500 } + ); + }); + + await exec("compose new topic", () => { + const date = `(${(+new Date())})`; + const title = `This is a new topic ${date}`; + const post = `I can write a new topic inside the smoke test! ${date} \n\n`; + + let promise = page.type("#reply-title", title); + + promise = promise.then(() => { + return page.type("#reply-control .d-editor-input", post); + }); + + return promise; + }); + + await exec("updates preview", () => { + return page.waitForSelector(".d-editor-preview p", { timeout: 500, visible: true }); + }); + + await exec("open upload modal", () => { + return page.click(".d-editor-button-bar .upload", { timeout: 500 }); + }); + + await exec("upload modal is open", () => { + let promise = page.waitForSelector("#filename-input", { timeout: 1500, visible: true }); + + promise.then(() => { + return page.click(".d-modal-cancel", { timeout: 500 }); + }); + + return promise; + }); + + await exec("submit the topic", () => { + return page.click(".submit-panel .create", { timeout: 500 }); + }); + + await exec("topic is created", () => { + return page.waitForSelector(".fancy-title", { timeout: 1500, visible: true }); + }); + + await exec("open the composer", () => { + return page.click(".post-controls:first-of-type .create", { timeout: 500 }); + }); + + await exec("composer is open", () => { + return page.waitForSelector("#reply-control .d-editor-input", { timeout: 500, visible: true }); + }); + + await exec("compose reply", () => { + const post = `I can even write a reply inside the smoke test ;) (${(+new Date())})`; + return page.type("#reply-control .d-editor-input", post); + }); + + await assert("waiting for the preview", () => { + let promise = page.waitForSelector(".d-editor-preview p", + { timeout: 500, visible: true } + ); + + promise = promise.then(() => { + return page.evaluate(() => { + return document.querySelector(".d-editor-preview").innerText; + }); + }); + + return promise; + }, output => { + return output.match("I can even write a reply"); + }); + + await exec("submit the topic", () => { + return page.click("#reply-control .create", { timeout: 6000 }); + }); + + await assert("reply is created", () => { + let promise = page.waitForSelector(".topic-post", { timeout: 500 }); + + promise = promise.then(() => { + return page.evaluate(() => { + return document.querySelectorAll(".topic-post").length; + }); + }); + + return promise; + }, output => { + return output === 2; + }); + } + + await browser.close(); + + console.log("ALL PASSED"); +})(); diff --git a/yarn.lock b/yarn.lock index ff9dfcbd242..fba73f4af84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,6 +18,12 @@ version "0.0.28" resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-0.0.28.tgz#5562519bc7963caca8abf7f128cae3b594d41d06" +agent-base@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.1.2.tgz#80fa6cde440f4dcf9af2617cf246099b5d99f0c8" + dependencies: + es6-promisify "^5.0.0" + async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" @@ -61,12 +67,55 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -debug@^2.6.8: +concat-stream@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +debug@2.6.9, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: ms "2.0.0" +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +es6-promise@^4.0.3: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.1.1.tgz#8811e90915d9a0dba36274f0b242dbda78f9c92a" + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + dependencies: + es6-promise "^4.0.3" + +extract-zip@^1.6.5: + version "1.6.6" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" + dependencies: + concat-stream "1.6.0" + debug "2.6.9" + mkdirp "0.5.0" + yauzl "2.4.1" + +fd-slicer@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + dependencies: + pend "~1.2.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -82,6 +131,13 @@ glob@^7.0.5: once "^1.3.0" path-is-absolute "^1.0.0" +https-proxy-agent@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.1.1.tgz#a7ce4382a1ba8266ee848578778122d491260fd9" + dependencies: + agent-base "^4.1.0" + debug "^3.1.0" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -89,7 +145,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.3, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -97,12 +153,20 @@ is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + lighthouse-logger@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.0.1.tgz#f073d83f7acbc96729bf100a121c8f006991ae61" dependencies: debug "^2.6.8" +mime@^1.3.4: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -113,6 +177,12 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" +mkdirp@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" + dependencies: + minimist "0.0.8" + mkdirp@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -133,28 +203,89 @@ path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + +proxy-from-env@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + +puppeteer@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-0.13.0.tgz#2e6956205f2c640964c2107f620ae1eef8bde8fd" + dependencies: + debug "^2.6.8" + extract-zip "^1.6.5" + https-proxy-agent "^2.1.0" + mime "^1.3.4" + progress "^2.0.0" + proxy-from-env "^1.0.0" + rimraf "^2.6.1" + ws "^3.0.0" + +readable-stream@^2.2.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + rimraf@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: glob "^7.0.5" -safe-buffer@~5.1.0: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -ws@3.3.x: +ws@3.3.x, ws@^3.0.0: version "3.3.2" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.2.tgz#96c1d08b3fefda1d5c1e33700d3bfaa9be2d5608" dependencies: async-limiter "~1.0.0" safe-buffer "~5.1.0" ultron "~1.1.0" + +yauzl@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" + dependencies: + fd-slicer "~1.0.1"