From dedeaaea3f515f83c27b34cefa796d1c1156e861 Mon Sep 17 00:00:00 2001 From: justin taft Date: Thu, 24 Sep 2015 23:11:05 -0700 Subject: [PATCH] Adds system tests w/ travis and appveyor ci intergration --- .gitignore | 1 + .travis.yml | 11 +++- appveyor.yml | 50 ++++++++++++++ project.clj | 7 +- script/light-test.bat | 4 ++ script/light-test.sh | 2 + script/run-tests.sh | 53 +++++++++++++++ src/lt/objs/dialogs.cljs | 36 +++++++--- src/lt/plugins/test_setup.cljs | 34 ++++++++++ test/lt/system_tests.clj | 117 +++++++++++++++++++++++++++++++++ test/lt/webdriver_helper.clj | 57 ++++++++++++++++ 11 files changed, 362 insertions(+), 10 deletions(-) create mode 100644 appveyor.yml create mode 100644 script/light-test.bat create mode 100755 script/light-test.sh create mode 100755 script/run-tests.sh create mode 100644 src/lt/plugins/test_setup.cljs create mode 100644 test/lt/system_tests.clj create mode 100644 test/lt/webdriver_helper.clj diff --git a/.gitignore b/.gitignore index 3a36a8926..c3bad7f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /deploy/core/node_modules/lighttable/bootstrap.js* .lein-deps-sum .lein-env +.lein-failures .lein-plugins/ .nrepl-port /builds/ diff --git a/.travis.yml b/.travis.yml index 0221b2eb4..1b3caba6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: clojure lein: lein -script: "! lein cljsbuild once 2>&1 | grep WARNING:" +script: + - ./script/build.sh + - ./script/run-tests.sh jdk: - oraclejdk8 notifications: @@ -11,3 +13,10 @@ notifications: - mike.j.innes@gmail.com - joshuafcole@gmail.com - jamie@scattered-thoughts.net +before_script: + - "export DISPLAY=:99.0" + - "sh -e /etc/init.d/xvfb start" +addons: + apt: + packages: + - chromium-browser diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..443f75706 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,50 @@ +environment: + global: + CYG_ROOT: C:\cygwin + CYG_MIRROR: http://cygwin.mirror.constant.com + CYG_CACHE: C:\cygwin\var\cache\setup + CYG_BASH: C:\cygwin\bin\bash + +cache: + - '%CYG_CACHE%' + +install: + - 'echo Setting up Cygwin dependencies' + - '%CYG_ROOT%\setup-x86.exe -qnNdO -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" -P autoconf -P automake -P bison -P gcc-core -P gcc-g++ -P mingw-runtime -P mingw-binutils -P mingw-gcc-core -P mingw-gcc-g++ -P mingw-pthreads -P mingw-w32api -P libtool -P make -P python -P gettext-devel -P gettext -P intltool -P libiconv -P pkg-config -P git -P wget -P curl -P unzip -P psmisc> NUL' + + - 'echo Check Cygwin setup' + - '%CYG_ROOT%/bin/bash -lc "cygcheck -dc cygwin"' + - 'echo Done setting up Cygwin' + + - install lein + - ps: | + $base = "https://raw.githubusercontent.com/technomancy/leiningen/" + $lein = $base + "stable/bin/lein.bat" + (new-object net.webclient).DownloadFile($lein, "c:/projects/lighttable/lein.bat") + - lein self-install + - 'echo Done installing lein' + #Add random location, this prevents qutation mark being added to + #the current directory's path + - cmd: 'set PATH="%PATH%;%cd%;c:\windows\system32"' + - cmd: 'echo %PATH%' + +build_script: + - cmd: 'echo Cygwin root is: %CYG_ROOT%' + - cmd: 'echo Build folder is: %APPVEYOR_BUILD_FOLDER%' + - cmd: 'echo Repo build branch is: %APPVEYOR_REPO_BRANCH%' + - cmd: 'echo Repo build commit is: %APPVEYOR_REPO_COMMIT%' + + - cmd: 'echo creating lein reference' + - '%CYG_ROOT%/bin/bash -lc "cd $APPVEYOR_BUILD_FOLDER; echo \"#!/bin/bash\" > lein.sh"' + - '%CYG_ROOT%/bin/bash -lc "cd $APPVEYOR_BUILD_FOLDER; echo \"./lein.bat \$@\" >> lein.sh"' + - '%CYG_ROOT%/bin/bash -lc "cd $APPVEYOR_BUILD_FOLDER; chmod a+x lein.sh"' + - '%CYG_ROOT%/bin/bash -lc "cd $APPVEYOR_BUILD_FOLDER; ln -s /cygdrive/c/projects/lighttable/lein.sh lein; ls -la"' + + - cmd: 'echo Running build.sh' + - '%CYG_ROOT%/bin/bash -lc "cd $APPVEYOR_BUILD_FOLDER; ./script/build.sh"' + - '%CYG_ROOT%/bin/bash -lc "cd $APPVEYOR_BUILD_FOLDER; ./script/run-tests.sh"' + + #DEBUG BEGIN, PAUSES BUILD PROCESS TO ALLOW FOR REMOTE DEBUGGING + #- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) + #- ps: throw "Failing tests" + #DBEUG END diff --git a/project.clj b/project.clj index 90a778083..b2bb95051 100644 --- a/project.clj +++ b/project.clj @@ -15,6 +15,11 @@ :output-to "deploy/core/node_modules/lighttable/bootstrap.js" :output-dir "deploy/core/node_modules/lighttable/cljs/" :pretty-print true}}]} - :plugins [[lein-cljsbuild "1.0.1"]] :source-paths ["src/"] + :profiles {:dev {:resource-paths ["test-deps"] + :dependencies [[clj-webdriver "0.7.2"] + [org.seleniumhq.selenium/selenium-java "2.47.0"]] + :plugins [[test2junit "1.1.2"] + [lein-cljsbuild "1.0.1"]] + }} ) diff --git a/script/light-test.bat b/script/light-test.bat new file mode 100644 index 000000000..0d7c9452d --- /dev/null +++ b/script/light-test.bat @@ -0,0 +1,4 @@ +set LTPATH=%~dp0..\ + +c:\cygwin\bin\bash.exe -lc "cd `cygpath -u \"$LTPATH\"`; ./script/light-test.sh" + diff --git a/script/light-test.sh b/script/light-test.sh new file mode 100755 index 000000000..d8bf45287 --- /dev/null +++ b/script/light-test.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./script/light.sh &> /tmp/lt-debug diff --git a/script/run-tests.sh b/script/run-tests.sh new file mode 100755 index 000000000..33f814fff --- /dev/null +++ b/script/run-tests.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +echo "Checking for chromedrive" +ISWINDOWS=0 +if [ "$(echo $(uname -s) | cut -c 1-9)" == "CYGWIN_NT" ]; then + CHROMEDRIVER_PATH=/tmp/chromedriver.exe + ISWINDOWS=1 +else + CHROMEDRIVER_PATH=/tmp/chromedriver +fi + + +if [ ! -f "$CHROMEDRIVER_PATH" ]; then + + # from: http://stackoverflow.com/a/17072017/142317 + if [ "$(uname)" == "Darwin" ]; then + URL="http://chromedriver.storage.googleapis.com/2.19/chromedriver_mac32.zip" + elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + URL="http://chromedriver.storage.googleapis.com/2.19/chromedriver_linux64.zip" + elif [ "$ISWINDOWS" == "1" ]; then + URL="http://chromedriver.storage.googleapis.com/2.19/chromedriver_win32.zip" + else + echo "Cannot detect a supported OS." + exit 1 + fi + + echo "Downloading & extracting chromedriver." + wget $URL -O /tmp/chromedriver.zip + unzip /tmp/chromedriver.zip -d /tmp +fi + +echo "Killing all chromedrivers & starting new instance". + + +killall "chromedriver" +killall "chromedriver.exe" + +chmod u+x $CHROMEDRIVER_PATH +$CHROMEDRIVER_PATH& +CHROME_PID=$! + +echo "Running tests" +lein with-profile dev test +RETURN_CODE=$? + +echo "===LIGHT TABLE DEBUG===" +cat /tmp/lt-debug + + +echo "Killing chrome" +kill $CHROME_PID + +exit $RETURN_CODE diff --git a/src/lt/objs/dialogs.cljs b/src/lt/objs/dialogs.cljs index 82b7df880..f6d74e6b4 100644 --- a/src/lt/objs/dialogs.cljs +++ b/src/lt/objs/dialogs.cljs @@ -8,16 +8,36 @@ (def remote (js/require "remote")) (def dialog (.require remote "dialog")) -(defn dir [obj event] - (let [files (.showOpenDialog dialog app/win #js {:properties #js ["openDirectory" "multiSelections"]})] +(defn set-show-file-dialog-fn! [f] (def show-file-dialog f)) + +(set-show-file-dialog-fn! + (fn [type options callback] + (if (= :open type) + (callback (.showOpenDialog dialog app/win options)) + (callback [(.showSaveDialog dialog app/win options)])))) + +(defn broadcast-file-selected [obj event files] (doseq [file files] - (object/raise obj event file)))) + (if file (object/raise obj event file)))) + +(defn dir [obj event] + (show-file-dialog + :open + #js {:properties #js ["openDirectory" "multiSelections"]} + (partial broadcast-file-selected obj event))) + (defn file [obj event] - (let [files (.showOpenDialog dialog app/win #js {:properties #js ["openFile" "multiSelections"]})] - (doseq [file files] - (object/raise obj event file)))) + (show-file-dialog + :open + #js {:properties #js ["openFile" "multiSelections"]} + (partial broadcast-file-selected obj event))) (defn save-as [obj event path] - (when-let [file (.showSaveDialog dialog app/win #js {:defaultPath path})] - (object/raise obj event file))) + (show-file-dialog + :save + #js {:defaultPath path} + (partial broadcast-file-selected obj event))) + + + diff --git a/src/lt/plugins/test_setup.cljs b/src/lt/plugins/test_setup.cljs new file mode 100644 index 000000000..47e74fab8 --- /dev/null +++ b/src/lt/plugins/test_setup.cljs @@ -0,0 +1,34 @@ +(ns lt.plugins.test-setup + "Mocks & extends light table components for auotmated testing." + (:require [clojure.string :as string] + [lt.objs/popup :as popup] + [lt.util.dom :as dom] + [lt.objs.command :as cmd])) + +(def debug-file-path-input (atom "")) + +(defn show-file-dialog-mock + "Mocks show-file-dialog so native file dialog does not show. + A popup dialog with a text field is shown instead. File paths + can be specified in the path fields, seperated by commas to donate + multiple files." + [type options callback] + (popup/popup! {:body [:div [:h1 "File Dialog Mock"] [:input {:type "text" :id "debug-file-path"}]] + :buttons + [ + { + :label "Okay" + :action #(swap! debug-file-path-input (fn [_] (dom/val (dom/$ "#debug-file-path")))) + :post-action #(callback (string/split @debug-file-path-input #",")) + } + ] + })) + +(cmd/command {:command :lt.testing.mock + :desc "Test Env: Mocks light table components for automated tests." + :exec #( + ;TODO error is thrown when evaling this, but it works... + (lt.objs.dialogs/set-show-file-dialog-fn! show-file-dialog-mock) + ) + }) + diff --git a/test/lt/system_tests.clj b/test/lt/system_tests.clj new file mode 100644 index 000000000..6060f6741 --- /dev/null +++ b/test/lt/system_tests.clj @@ -0,0 +1,117 @@ +(ns lt.system-tests + (:import + (org.openqa.selenium Keys) + (java.io File) + (java.nio.file Path) + (java.nio.file Paths) + (java.nio.file Files) + (java.nio.file.attribute FileAttribute) + (java.nio.file OpenOption) + (java.io File)) + (:require + [lt.webdriver-helper :as lt-webdriver-helper] + [clj-webdriver.taxi :as taxi] + [clj-webdriver.core :as core] + [clj-webdriver.element :as element] + [clojure.string :as str] + [clojure.test :refer :all :as test])) + + +(defn create-temporary-file [] +(-> (Files/createTempFile (String. "temp-file-name") (String. ".tmp") (into-array FileAttribute [])) + (.toAbsolutePath) + (.toString))) + + +(defn read-file-contents [path] +(-> (Paths/get path (into-array String [])) + (Files/readAllBytes) + (String. ))) + + + +(defn helper-open-side-panel [] + (taxi/send-keys (taxi/find-element {:css "body"}) + (Keys/chord [ + (core/key-code :control) + (core/key-code :space)]))) + +(defn helper-select-side-panel-option [name] + (-> (taxi/switch-to-active) + (.sendKeys (into-array CharSequence (list name (core/key-code :enter)))))) + +;TODO implode files vector, comma seperated values +(defn helper-select-files [files] + (taxi/send-keys {:css "#debug-file-path"} (str (first files) "\n"))) + +(defn get-file-name-from-path [path] + (-> (File. path) + (.getName))) + +(defn lt-fixture [func] + (lt-webdriver-helper/connect-to-light-table!) + (func)) + +(use-fixtures :each lt-fixture) + +(deftest new-file + + ;TODO write better method to test if + ;light table is finished initilaizing + (Thread/sleep 20000) + + + ;Create temporary file + (def temp-file-path (create-temporary-file)) + + ;Enable test mocks for testing + (helper-open-side-panel) + (helper-select-side-panel-option "Test Env:") + + ;Create new file + (helper-open-side-panel) + (helper-select-side-panel-option "new file") + + ;Type some content + (-> (element/init-element (taxi/switch-to-active)) + (taxi/send-keys "File Persistence Test")) + + (Thread/sleep 1000) + ;Save file + (helper-open-side-panel) + (helper-select-side-panel-option "Save File") + (helper-select-files [temp-file-path]) + + ;Verify file contents + (Thread/sleep 1000) + (is (= "File Persistence Test" (read-file-contents temp-file-path))) + + ;Close Tab + (core/->actions (lt-webdriver-helper/get-webdriver) + (core/move-to-element + (taxi/find-element {:tag :span :class "file-name" :text (get-file-name-from-path temp-file-path) }))) + + (taxi/click {:css ".tabset.active .list li.active .tab-close"}) + + ;Open file + (helper-open-side-panel) + (helper-select-side-panel-option "Open File") + (helper-select-files [temp-file-path]) + + (Thread/sleep 500) + + ;assert editor has correct text + (is (= "File Persistence Test" + (taxi/text {:css ".CodeMirror-code span"}))) + + (is (= "File Persistence Test I SHOULD FAIL" + (taxi/text {:css ".CodeMirror-code span"}))) + + + ;Close Tab + (Thread/sleep 1000) + (core/->actions (lt-webdriver-helper/get-webdriver) + (core/move-to-element + (taxi/find-element {:tag :span :class "file-name" :text (get-file-name-from-path temp-file-path) }))) + (taxi/click {:css ".tabset.active .list li.active .tab-close"}) +) diff --git a/test/lt/webdriver_helper.clj b/test/lt/webdriver_helper.clj new file mode 100644 index 000000000..e898af611 --- /dev/null +++ b/test/lt/webdriver_helper.clj @@ -0,0 +1,57 @@ +(ns lt.webdriver-helper + (:import + (java.net URL) + (java.io File) + (org.openqa.selenium.remote DesiredCapabilities) + (org.openqa.selenium.remote RemoteWebDriver) + (org.openqa.selenium.chrome ChromeOptions)) + (:require + [clj-webdriver.taxi :as taxi] + [clojure.java.shell :only [sh] :as shell] + [clj-webdriver.driver :as driver])) + + +(def chromeOptions { + "debuggerAddress" "127.0.0.1:8315" +}) + +(def capabilities (DesiredCapabilities/chrome)) +(.setCapability capabilities ChromeOptions/CAPABILITY chromeOptions) +(.setCapability capabilities "browserName" "electron") + + +(defn log [msg] + (println msg)) + + +(defn get-project-directory [] + (System/getProperty "user.dir")) + + +(defn get-lt-test-launch-script-location [os-name] + "Returns script that launches light table for tests." + (str (get-project-directory) File/separator "script" File/separator "light-test" + (if (re-matches #"^(?i)windows.*|cygwin.*" os-name) + ".bat" + ".sh"))) + +(defn start-lighttable-async [] + (-> (Thread. (fn[] + (log (str "OS is " (System/getProperty "os.name"))) + (Thread/sleep 5000) + (shell/sh (get-lt-test-launch-script-location (System/getProperty "os.name"))))) + (.start))) + +(defn connect-to-light-table! [] + "Connects to light table" + ;TODO check if we are already connected to light-table to avoid re-connecting? + (start-lighttable-async) + (-> (RemoteWebDriver. (URL. "http://127.0.0.1:9515") capabilities) + (driver/init-driver) + (taxi/set-driver!))) + +(defn get-webdriver [] + taxi/*driver*) + + +