From de774ba03ec6bd720d6844cb8f4325e657d2b9c5 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Mon, 12 Nov 2018 14:53:36 -0500 Subject: [PATCH] Integration tests (#170) --- CHANGELOG.md | 9 ++ e2e/main.go | 49 +++---- e2e/pages/dynamic/components/app.js | 29 ++++ e2e/pages/dynamic/components/layout.js | 27 ++++ .../dynamic/components/pages/forms/index.js | 124 ++++++++++++++++++ e2e/pages/dynamic/components/pages/index.js | 8 ++ e2e/pages/dynamic/index.css | 4 + e2e/pages/dynamic/index.html | 21 +++ e2e/pages/dynamic/index.js | 9 ++ e2e/pages/dynamic/utils/qs.js | 82 ++++++++++++ .../{bootstrap => static}/assets/analytics.js | 0 .../assets/bootstrap.min.css | 0 .../assets/bootstrap.min.js | 0 .../{bootstrap => static}/assets/carbon.js | 0 .../{bootstrap => static}/assets/docs.min.css | 0 .../{bootstrap => static}/assets/docs.min.js | 0 .../assets/docsearch.min.css | 0 .../assets/docsearch.min.js | 0 .../assets/jquery-3.3.1.slim.min.js | 0 .../assets/popper.min.js | 0 e2e/pages/{bootstrap => static}/grid.html | 0 e2e/pages/{bootstrap => static}/media.html | 0 e2e/pages/{bootstrap => static}/overview.html | 0 .../{bootstrap => static}/utilities.html | 0 e2e/runner/runner.go | 10 +- e2e/server/server.go | 2 + .../{inner_html.fql => doc_inner_html.fql} | 2 +- ...er_html_all.fql => doc_inner_html_all.fql} | 2 +- e2e/tests/doc_inner_html_all_d.fql | 12 ++ e2e/tests/doc_inner_html_d.fql | 10 ++ .../{inner_text.fql => doc_inner_text.fql} | 2 +- ...er_text_all.fql => doc_inner_text_all.fql} | 2 +- e2e/tests/doc_inner_text_all_d.fql | 16 +++ e2e/tests/doc_inner_text_d.fql | 10 ++ e2e/tests/doc_input_text_d.fql | 10 ++ e2e/tests/doc_select_multi_d.fql | 9 ++ e2e/tests/doc_select_single_d.fql | 9 ++ e2e/tests/el_input_text_d.fql | 11 ++ e2e/tests/el_select_multi_d.fql | 10 ++ e2e/tests/el_select_single_d.fql | 10 ++ e2e/tests/inner_html_dynamic.fql | 10 -- e2e/tests/inner_text_dynamic.fql | 9 -- e2e/tests/{page-load.fql => page_load.fql} | 2 +- e2e/tests/page_load_d.fql | 4 + pkg/html/dynamic/document.go | 55 +++++++- pkg/html/dynamic/element.go | 74 +++++++++++ pkg/html/dynamic/helpers.go | 35 ++++- pkg/stdlib/html/input.go | 4 +- pkg/stdlib/html/lib.go | 1 + pkg/stdlib/html/select.go | 72 ++++++++++ 50 files changed, 695 insertions(+), 60 deletions(-) create mode 100644 e2e/pages/dynamic/components/app.js create mode 100644 e2e/pages/dynamic/components/layout.js create mode 100644 e2e/pages/dynamic/components/pages/forms/index.js create mode 100644 e2e/pages/dynamic/components/pages/index.js create mode 100644 e2e/pages/dynamic/index.css create mode 100644 e2e/pages/dynamic/index.html create mode 100644 e2e/pages/dynamic/index.js create mode 100644 e2e/pages/dynamic/utils/qs.js rename e2e/pages/{bootstrap => static}/assets/analytics.js (100%) rename e2e/pages/{bootstrap => static}/assets/bootstrap.min.css (100%) rename e2e/pages/{bootstrap => static}/assets/bootstrap.min.js (100%) rename e2e/pages/{bootstrap => static}/assets/carbon.js (100%) rename e2e/pages/{bootstrap => static}/assets/docs.min.css (100%) rename e2e/pages/{bootstrap => static}/assets/docs.min.js (100%) rename e2e/pages/{bootstrap => static}/assets/docsearch.min.css (100%) rename e2e/pages/{bootstrap => static}/assets/docsearch.min.js (100%) rename e2e/pages/{bootstrap => static}/assets/jquery-3.3.1.slim.min.js (100%) rename e2e/pages/{bootstrap => static}/assets/popper.min.js (100%) rename e2e/pages/{bootstrap => static}/grid.html (100%) rename e2e/pages/{bootstrap => static}/media.html (100%) rename e2e/pages/{bootstrap => static}/overview.html (100%) rename e2e/pages/{bootstrap => static}/utilities.html (100%) rename e2e/tests/{inner_html.fql => doc_inner_html.fql} (89%) rename e2e/tests/{inner_html_all.fql => doc_inner_html_all.fql} (84%) create mode 100644 e2e/tests/doc_inner_html_all_d.fql create mode 100644 e2e/tests/doc_inner_html_d.fql rename e2e/tests/{inner_text.fql => doc_inner_text.fql} (87%) rename e2e/tests/{inner_text_all.fql => doc_inner_text_all.fql} (97%) create mode 100644 e2e/tests/doc_inner_text_all_d.fql create mode 100644 e2e/tests/doc_inner_text_d.fql create mode 100644 e2e/tests/doc_input_text_d.fql create mode 100644 e2e/tests/doc_select_multi_d.fql create mode 100644 e2e/tests/doc_select_single_d.fql create mode 100644 e2e/tests/el_input_text_d.fql create mode 100644 e2e/tests/el_select_multi_d.fql create mode 100644 e2e/tests/el_select_single_d.fql delete mode 100644 e2e/tests/inner_html_dynamic.fql delete mode 100644 e2e/tests/inner_text_dynamic.fql rename e2e/tests/{page-load.fql => page_load.fql} (52%) create mode 100644 e2e/tests/page_load_d.fql create mode 100644 pkg/stdlib/html/select.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 625e822b..d68ee8e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ## Changelog +### 0.5.0 +#### Added +- DateTime functions. + +#### Fixed +- Unable to define variables and make function calls before FILTER, SORT and etc statements. +- ``INNER_HTML`` returns outer HTML instead for dynamic elements. +- ``INNER_TEXT`` returns HTML instead from dynamic elements. + ### 0.4.0 #### Added - ``COLLECT`` keyword [#141](https://github.com/MontFerret/ferret/pull/141) diff --git a/e2e/main.go b/e2e/main.go index f47d2d5d..8f084523 100644 --- a/e2e/main.go +++ b/e2e/main.go @@ -1,14 +1,14 @@ package main import ( - "context" "flag" "fmt" + "os" + "path/filepath" + "github.com/MontFerret/ferret/e2e/runner" "github.com/MontFerret/ferret/e2e/server" "github.com/rs/zerolog" - "os" - "path/filepath" ) var ( @@ -24,12 +24,6 @@ var ( "root directory with test pages", ) - port = flag.Uint64( - "port", - 8080, - "server port", - ) - cdp = flag.String( "cdp", "http://0.0.0.0:9222", @@ -42,17 +36,27 @@ func main() { logger := zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}) - s := server.New(server.Settings{ - Port: *port, - Dir: *pagesDir, + staticPort := uint64(8080) + static := server.New(server.Settings{ + Port: staticPort, + Dir: filepath.Join(*pagesDir, "static"), }) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + dynamicPort := uint64(8081) + dynamic := server.New(server.Settings{ + Port: dynamicPort, + Dir: filepath.Join(*pagesDir, "dynamic"), + }) go func() { - if err := s.Start(); err != nil { - logger.Info().Timestamp().Msg("shutting down the server") + if err := static.Start(); err != nil { + logger.Info().Timestamp().Msg("shutting down the static pages server") + } + }() + + go func() { + if err := dynamic.Start(); err != nil { + logger.Info().Timestamp().Msg("shutting down the dynamic pages server") } }() @@ -71,18 +75,17 @@ func main() { } r := runner.New(logger, runner.Settings{ - ServerAddress: fmt.Sprintf("http://0.0.0.0:%d", *port), - CDPAddress: *cdp, - Dir: *testsDir, + StaticServerAddress: fmt.Sprintf("http://0.0.0.0:%d", staticPort), + DynamicServerAddress: fmt.Sprintf("http://0.0.0.0:%d", dynamicPort), + CDPAddress: *cdp, + Dir: *testsDir, }) err := r.Run() - if err := s.Stop(ctx); err != nil { - logger.Fatal().Timestamp().Err(err).Msg("failed to stop server") - } - if err != nil { os.Exit(1) } + + os.Exit(0) } diff --git a/e2e/pages/dynamic/components/app.js b/e2e/pages/dynamic/components/app.js new file mode 100644 index 00000000..c17c94c4 --- /dev/null +++ b/e2e/pages/dynamic/components/app.js @@ -0,0 +1,29 @@ +import Layout from './layout.js'; +import IndexPage from './pages/index.js'; +import FormsPage from './pages/forms/index.js'; + +const e = React.createElement; +const Router = ReactRouter.Router; +const Switch = ReactRouter.Switch; +const Route = ReactRouter.Route; +const Redirect = ReactRouter.Redirect; +const createBrowserHistory = History.createBrowserHistory; + +export default function AppComponent({ redirect = null}) { + return e(Router, { history: createBrowserHistory() }, + e(Layout, null, [ + e(Switch, null, [ + e(Route, { + path: '/', + exact: true, + component: IndexPage + }), + e(Route, { + path: '/forms', + component: FormsPage + }) + ]), + redirect ? e(Redirect, { to: redirect }) : null + ]) + ) +} \ No newline at end of file diff --git a/e2e/pages/dynamic/components/layout.js b/e2e/pages/dynamic/components/layout.js new file mode 100644 index 00000000..ab81a662 --- /dev/null +++ b/e2e/pages/dynamic/components/layout.js @@ -0,0 +1,27 @@ +const e = React.createElement; +const NavLink = ReactRouterDOM.NavLink; + +export default function Layout({ children }) { + return e("div", { id: "layout"}, [ + e("nav", { className: "navbar navbar-expand-md navbar-dark bg-dark mb-4" }, [ + e(NavLink, { className: "navbar-brand", to: "/"}, "Ferret"), + e("button", { className: "navbar-toggler", type: "button"}, [ + e("span", { className: "navbar-toggler-icon" }) + ]), + e("div", { className: "collapse navbar-collapse" }, [ + e("ul", { className: "navbar-nav mr-auto" }, [ + e("li", { className: "nav-item"}, [ + e(NavLink, { className: "nav-link", to: "/forms" }, "Forms") + ]), + e("li", { className: "nav-item"}, [ + e(NavLink, { className: "nav-link", to: "/navigation" }, "Navigation") + ]), + e("li", { className: "nav-item"}, [ + e(NavLink, { className: "nav-link", to: "/events" }, "Events") + ]) + ]) + ]) + ]), + e("main", { className: "container"}, children) + ]) +} \ No newline at end of file diff --git a/e2e/pages/dynamic/components/pages/forms/index.js b/e2e/pages/dynamic/components/pages/forms/index.js new file mode 100644 index 00000000..df30fa04 --- /dev/null +++ b/e2e/pages/dynamic/components/pages/forms/index.js @@ -0,0 +1,124 @@ +const e = React.createElement; + +export default class FormsPage extends React.Component { + constructor(props) { + super(props); + + this.state = { + textInput: "", + select: "", + multiSelect: "", + textarea: "" + }; + + this.handleTextInput = (evt) => { + evt.preventDefault(); + + this.setState({ + textInput: evt.target.value + }); + }; + + this.handleSelect = (evt) => { + evt.preventDefault(); + + this.setState({ + select: evt.target.value + }); + }; + + this.handleMultiSelect = (evt) => { + evt.preventDefault(); + + this.setState({ + multiSelect: Array.prototype.map.call(evt.target.selectedOptions, i => i.value).join(", ") + }); + }; + + this.handleTtextarea = (evt) => { + evt.preventDefault(); + + this.setState({ + textarea: evt.target.value + }); + } + } + + render() { + return e("form", null, [ + e("div", { className: "form-group" }, [ + e("label", null, "Text input"), + e("input", { + id: "text_input", + type: "text", + className: "form-control", + onChange: this.handleTextInput + }), + e("small", { + id: "text_output", + className: "form-text text-muted" + }, + this.state.textInput + ) + ]), + e("div", { className: "form-group" }, [ + e("label", null, "Select"), + e("select", { + id: "select_input", + className: "form-control", + onChange: this.handleSelect + }, + [ + e("option", null, 1), + e("option", null, 2), + e("option", null, 3), + e("option", null, 4), + e("option", null, 5), + ] + ), + e("small", { + id: "select_output", + className: "form-text text-muted" + }, this.state.select + ) + ]), + e("div", { className: "form-group" }, [ + e("label", null, "Multi select"), + e("select", { + id: "multi_select_input", + multiple: true, + className: "form-control", + onChange: this.handleMultiSelect + }, + [ + e("option", null, 1), + e("option", null, 2), + e("option", null, 3), + e("option", null, 4), + e("option", null, 5), + ] + ), + e("small", { + id: "multi_select_output", + className: "form-text text-muted" + }, this.state.multiSelect + ) + ]), + e("div", { className: "form-group" }, [ + e("label", null, "Textarea"), + e("textarea", { + id: "textarea_input", + rows:"5", + className: "form-control", + onChange: this.handleTtextarea + } + ), + e("small", { + id: "textarea_output", + className: "form-text text-muted" + }, this.state.textarea + ) + ]), + ]) + } +} \ No newline at end of file diff --git a/e2e/pages/dynamic/components/pages/index.js b/e2e/pages/dynamic/components/pages/index.js new file mode 100644 index 00000000..e114e658 --- /dev/null +++ b/e2e/pages/dynamic/components/pages/index.js @@ -0,0 +1,8 @@ +const e = React.createElement; + +export default function IndexPage() { + return e("div", { className: "jumbotron" }, [ + e("div", null, e("h1", null, "Welcome to Ferret E2E test page!")), + e("div", null, e("p", { className: "lead" }, "It has several pages for testing different possibilities of the library")) + ]) +} \ No newline at end of file diff --git a/e2e/pages/dynamic/index.css b/e2e/pages/dynamic/index.css new file mode 100644 index 00000000..c018d8a7 --- /dev/null +++ b/e2e/pages/dynamic/index.css @@ -0,0 +1,4 @@ +/* Show it's not fixed to the top */ +body { + min-height: 75rem; +} diff --git a/e2e/pages/dynamic/index.html b/e2e/pages/dynamic/index.html new file mode 100644 index 00000000..d854a8b9 --- /dev/null +++ b/e2e/pages/dynamic/index.html @@ -0,0 +1,21 @@ + + + + + + Ferret E2E SPA + + + + + + +
+ + + + + + + + diff --git a/e2e/pages/dynamic/index.js b/e2e/pages/dynamic/index.js new file mode 100644 index 00000000..7edcf959 --- /dev/null +++ b/e2e/pages/dynamic/index.js @@ -0,0 +1,9 @@ +import AppComponent from "./components/app.js"; +import { parse } from "./utils/qs.js"; + +const qs = parse(location.search); + +ReactDOM.render( + React.createElement(AppComponent, qs), + document.getElementById("root") +); diff --git a/e2e/pages/dynamic/utils/qs.js b/e2e/pages/dynamic/utils/qs.js new file mode 100644 index 00000000..76042158 --- /dev/null +++ b/e2e/pages/dynamic/utils/qs.js @@ -0,0 +1,82 @@ +'use strict'; + +var has = Object.prototype.hasOwnProperty + , undef; + +/** + * Decode a URI encoded string. + * + * @param {String} input The URI encoded string. + * @returns {String} The decoded string. + * @api private + */ +function decode(input) { + return decodeURIComponent(input.replace(/\+/g, ' ')); +} + +/** + * Simple query string parser. + * + * @param {String} query The query string that needs to be parsed. + * @returns {Object} + * @api public + */ +export function parse(query) { + var parser = /([^=?&]+)=?([^&]*)/g + , result = {} + , part; + + while (part = parser.exec(query)) { + var key = decode(part[1]) + , value = decode(part[2]); + + // + // Prevent overriding of existing properties. This ensures that build-in + // methods like `toString` or __proto__ are not overriden by malicious + // querystrings. + // + if (key in result) continue; + result[key] = value; + } + + return result; +} + +/** + * Transform a query string to an object. + * + * @param {Object} obj Object that should be transformed. + * @param {String} prefix Optional prefix. + * @returns {String} + * @api public + */ +export function stringify(obj, prefix) { + prefix = prefix || ''; + + var pairs = [] + , value + , key; + + // + // Optionally prefix with a '?' if needed + // + if ('string' !== typeof prefix) prefix = '?'; + + for (key in obj) { + if (has.call(obj, key)) { + value = obj[key]; + + // + // Edge cases where we actually want to encode the value to an empty + // string instead of the stringified value. + // + if (!value && (value === null || value === undef || isNaN(value))) { + value = ''; + } + + pairs.push(encodeURIComponent(key) +'='+ encodeURIComponent(value)); + } + } + + return pairs.length ? prefix + pairs.join('&') : ''; +} \ No newline at end of file diff --git a/e2e/pages/bootstrap/assets/analytics.js b/e2e/pages/static/assets/analytics.js similarity index 100% rename from e2e/pages/bootstrap/assets/analytics.js rename to e2e/pages/static/assets/analytics.js diff --git a/e2e/pages/bootstrap/assets/bootstrap.min.css b/e2e/pages/static/assets/bootstrap.min.css similarity index 100% rename from e2e/pages/bootstrap/assets/bootstrap.min.css rename to e2e/pages/static/assets/bootstrap.min.css diff --git a/e2e/pages/bootstrap/assets/bootstrap.min.js b/e2e/pages/static/assets/bootstrap.min.js similarity index 100% rename from e2e/pages/bootstrap/assets/bootstrap.min.js rename to e2e/pages/static/assets/bootstrap.min.js diff --git a/e2e/pages/bootstrap/assets/carbon.js b/e2e/pages/static/assets/carbon.js similarity index 100% rename from e2e/pages/bootstrap/assets/carbon.js rename to e2e/pages/static/assets/carbon.js diff --git a/e2e/pages/bootstrap/assets/docs.min.css b/e2e/pages/static/assets/docs.min.css similarity index 100% rename from e2e/pages/bootstrap/assets/docs.min.css rename to e2e/pages/static/assets/docs.min.css diff --git a/e2e/pages/bootstrap/assets/docs.min.js b/e2e/pages/static/assets/docs.min.js similarity index 100% rename from e2e/pages/bootstrap/assets/docs.min.js rename to e2e/pages/static/assets/docs.min.js diff --git a/e2e/pages/bootstrap/assets/docsearch.min.css b/e2e/pages/static/assets/docsearch.min.css similarity index 100% rename from e2e/pages/bootstrap/assets/docsearch.min.css rename to e2e/pages/static/assets/docsearch.min.css diff --git a/e2e/pages/bootstrap/assets/docsearch.min.js b/e2e/pages/static/assets/docsearch.min.js similarity index 100% rename from e2e/pages/bootstrap/assets/docsearch.min.js rename to e2e/pages/static/assets/docsearch.min.js diff --git a/e2e/pages/bootstrap/assets/jquery-3.3.1.slim.min.js b/e2e/pages/static/assets/jquery-3.3.1.slim.min.js similarity index 100% rename from e2e/pages/bootstrap/assets/jquery-3.3.1.slim.min.js rename to e2e/pages/static/assets/jquery-3.3.1.slim.min.js diff --git a/e2e/pages/bootstrap/assets/popper.min.js b/e2e/pages/static/assets/popper.min.js similarity index 100% rename from e2e/pages/bootstrap/assets/popper.min.js rename to e2e/pages/static/assets/popper.min.js diff --git a/e2e/pages/bootstrap/grid.html b/e2e/pages/static/grid.html similarity index 100% rename from e2e/pages/bootstrap/grid.html rename to e2e/pages/static/grid.html diff --git a/e2e/pages/bootstrap/media.html b/e2e/pages/static/media.html similarity index 100% rename from e2e/pages/bootstrap/media.html rename to e2e/pages/static/media.html diff --git a/e2e/pages/bootstrap/overview.html b/e2e/pages/static/overview.html similarity index 100% rename from e2e/pages/bootstrap/overview.html rename to e2e/pages/static/overview.html diff --git a/e2e/pages/bootstrap/utilities.html b/e2e/pages/static/utilities.html similarity index 100% rename from e2e/pages/bootstrap/utilities.html rename to e2e/pages/static/utilities.html diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go index e82c2486..d61826dd 100644 --- a/e2e/runner/runner.go +++ b/e2e/runner/runner.go @@ -14,9 +14,10 @@ import ( type ( Settings struct { - ServerAddress string - CDPAddress string - Dir string + StaticServerAddress string + DynamicServerAddress string + CDPAddress string + Dir string } Result struct { @@ -129,7 +130,8 @@ func (r *Runner) runQuery(c *compiler.FqlCompiler, name, script string) Result { out, err := p.Run( context.Background(), runtime.WithBrowser(r.settings.CDPAddress), - runtime.WithParam("server", r.settings.ServerAddress), + runtime.WithParam("static", r.settings.StaticServerAddress), + runtime.WithParam("dynamic", r.settings.DynamicServerAddress), ) duration := time.Now().Sub(start) diff --git a/e2e/server/server.go b/e2e/server/server.go index 80964aae..dc3e1bc1 100644 --- a/e2e/server/server.go +++ b/e2e/server/server.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/labstack/echo" + "path/filepath" ) type ( @@ -23,6 +24,7 @@ func New(settings Settings) *Server { e.HideBanner = true e.Static("/", settings.Dir) + e.File("/", filepath.Join(settings.Dir, "index.html")) return &Server{e, settings} } diff --git a/e2e/tests/inner_html.fql b/e2e/tests/doc_inner_html.fql similarity index 89% rename from e2e/tests/inner_html.fql rename to e2e/tests/doc_inner_html.fql index aeebfb78..b03e2cab 100644 --- a/e2e/tests/inner_html.fql +++ b/e2e/tests/doc_inner_html.fql @@ -1,4 +1,4 @@ -LET url = @server + '/bootstrap/overview.html' +LET url = @static + '/overview.html' LET doc = DOCUMENT(url) LET expected = '
  • Containers
  • Responsive breakpoints
  • Z-index
  • ' diff --git a/e2e/tests/inner_html_all.fql b/e2e/tests/doc_inner_html_all.fql similarity index 84% rename from e2e/tests/inner_html_all.fql rename to e2e/tests/doc_inner_html_all.fql index e31402b4..d8800cb3 100644 --- a/e2e/tests/inner_html_all.fql +++ b/e2e/tests/doc_inner_html_all.fql @@ -1,4 +1,4 @@ -LET url = @server + '/bootstrap/overview.html' +LET url = @static + '/overview.html' LET doc = DOCUMENT(url) LET expected = [ diff --git a/e2e/tests/doc_inner_html_all_d.fql b/e2e/tests/doc_inner_html_all_d.fql new file mode 100644 index 00000000..6944c100 --- /dev/null +++ b/e2e/tests/doc_inner_html_all_d.fql @@ -0,0 +1,12 @@ +LET url = @dynamic +LET doc = DOCUMENT(url, true) + +WAIT_ELEMENT(doc, "#layout") + +LET expected = [ +'

    Welcome to Ferret E2E test page!

    ', +'

    It has several pages for testing different possibilities of the library

    ' +] +LET actual = INNER_HTML_ALL(doc, '#root > div > main > div > *') + +RETURN EXPECT(expected, actual) \ No newline at end of file diff --git a/e2e/tests/doc_inner_html_d.fql b/e2e/tests/doc_inner_html_d.fql new file mode 100644 index 00000000..538683da --- /dev/null +++ b/e2e/tests/doc_inner_html_d.fql @@ -0,0 +1,10 @@ +LET url = @dynamic +LET doc = DOCUMENT(url, true) +LET selector = '#root > div > main > div' + +WAIT_ELEMENT(doc, "#layout") + +LET expected = '

    Welcome to Ferret E2E test page!

    It has several pages for testing different possibilities of the library

    ' +LET actual = INNER_HTML(doc, selector) + +RETURN EXPECT(REGEXP_REPLACE(expected, '\s', ''), REGEXP_REPLACE(TRIM(actual), '(\n|\s)', '')) \ No newline at end of file diff --git a/e2e/tests/inner_text.fql b/e2e/tests/doc_inner_text.fql similarity index 87% rename from e2e/tests/inner_text.fql rename to e2e/tests/doc_inner_text.fql index e70b900b..837eafea 100644 --- a/e2e/tests/inner_text.fql +++ b/e2e/tests/doc_inner_text.fql @@ -1,4 +1,4 @@ -LET url = @server + '/bootstrap/overview.html' +LET url = @static + '/overview.html' LET doc = DOCUMENT(url) LET expected = "Components and options for laying out your Bootstrap project, including wrapping containers, a powerful grid system, a flexible media object, and responsive utility classes." diff --git a/e2e/tests/inner_text_all.fql b/e2e/tests/doc_inner_text_all.fql similarity index 97% rename from e2e/tests/inner_text_all.fql rename to e2e/tests/doc_inner_text_all.fql index d4cb173d..9dcbd50b 100644 --- a/e2e/tests/inner_text_all.fql +++ b/e2e/tests/doc_inner_text_all.fql @@ -1,4 +1,4 @@ -LET url = @server + '/bootstrap/grid.html' +LET url = @static + '/grid.html' LET doc = DOCUMENT(url) LET expected = [ diff --git a/e2e/tests/doc_inner_text_all_d.fql b/e2e/tests/doc_inner_text_all_d.fql new file mode 100644 index 00000000..7a638ef8 --- /dev/null +++ b/e2e/tests/doc_inner_text_all_d.fql @@ -0,0 +1,16 @@ +LET url = @dynamic +LET doc = DOCUMENT(url, true) +LET selector = '#root > div > main > div > *' + +WAIT_ELEMENT(doc, "#layout") + +LET expected = [ + 'Welcome to Ferret E2E test page!', + 'It has several pages for testing different possibilities of the library' +] +LET actual = ( + FOR str IN INNER_TEXT_ALL(doc, selector) + RETURN REGEXP_REPLACE(TRIM(str), '\n', '') +) + +RETURN EXPECT(expected, actual) \ No newline at end of file diff --git a/e2e/tests/doc_inner_text_d.fql b/e2e/tests/doc_inner_text_d.fql new file mode 100644 index 00000000..4bc6977b --- /dev/null +++ b/e2e/tests/doc_inner_text_d.fql @@ -0,0 +1,10 @@ +LET url = @dynamic +LET doc = DOCUMENT(url, true) +LET selector = '#root > div > main > div h1' + +WAIT_ELEMENT(doc, "#layout") + +LET expected = 'Welcome to Ferret E2E test page!' +LET actual = INNER_TEXT(doc, selector) + +RETURN EXPECT(REGEXP_REPLACE(expected, '\s', ''), REGEXP_REPLACE(TRIM(actual), '(\n|\s)', '')) \ No newline at end of file diff --git a/e2e/tests/doc_input_text_d.fql b/e2e/tests/doc_input_text_d.fql new file mode 100644 index 00000000..0d2605d3 --- /dev/null +++ b/e2e/tests/doc_input_text_d.fql @@ -0,0 +1,10 @@ +LET url = @dynamic + "?redirect=/forms" +LET doc = DOCUMENT(url, true) + +WAIT_ELEMENT(doc, "form") + +LET output = ELEMENT(doc, "#text_output") + +INPUT(doc, "#text_input", "foo") + +RETURN EXPECT(output.innerText, "foo") \ No newline at end of file diff --git a/e2e/tests/doc_select_multi_d.fql b/e2e/tests/doc_select_multi_d.fql new file mode 100644 index 00000000..2dbc18d5 --- /dev/null +++ b/e2e/tests/doc_select_multi_d.fql @@ -0,0 +1,9 @@ +LET url = @dynamic + "?redirect=/forms" +LET doc = DOCUMENT(url, true) + +WAIT_ELEMENT(doc, "form") + +LET output = ELEMENT(doc, "#multi_select_output") +LET result = SELECT(doc, "#multi_select_input", ["1", "2", "4"]) + +RETURN EXPECT(output.innerText, "1, 2, 4") + EXPECT(JSON_STRINGIFY(result), '["1","2","4"]') \ No newline at end of file diff --git a/e2e/tests/doc_select_single_d.fql b/e2e/tests/doc_select_single_d.fql new file mode 100644 index 00000000..6623e097 --- /dev/null +++ b/e2e/tests/doc_select_single_d.fql @@ -0,0 +1,9 @@ +LET url = @dynamic + "?redirect=/forms" +LET doc = DOCUMENT(url, true) + +WAIT_ELEMENT(doc, "form") + +LET output = ELEMENT(doc, "#select_output") +LET result = SELECT(doc, "#select_input", ["4"]) + +RETURN EXPECT(output.innerText, "4") + EXPECT(JSON_STRINGIFY(result), '["4"]') \ No newline at end of file diff --git a/e2e/tests/el_input_text_d.fql b/e2e/tests/el_input_text_d.fql new file mode 100644 index 00000000..79301ab8 --- /dev/null +++ b/e2e/tests/el_input_text_d.fql @@ -0,0 +1,11 @@ +LET url = @dynamic + "?redirect=/forms" +LET doc = DOCUMENT(url, true) + +WAIT_ELEMENT(doc, "form") + +LET input = ELEMENT(doc, "#text_input") +LET output = ELEMENT(doc, "#text_output") + +INPUT(input, "foo") + +RETURN EXPECT(output.innerText, "foo") \ No newline at end of file diff --git a/e2e/tests/el_select_multi_d.fql b/e2e/tests/el_select_multi_d.fql new file mode 100644 index 00000000..d0332498 --- /dev/null +++ b/e2e/tests/el_select_multi_d.fql @@ -0,0 +1,10 @@ +LET url = @dynamic + "?redirect=/forms" +LET doc = DOCUMENT(url, true) + +WAIT_ELEMENT(doc, "form") + +LET input = ELEMENT(doc, "#multi_select_input") +LET output = ELEMENT(doc, "#multi_select_output") +LET result = SELECT(input, ["1", "2", "4"]) + +RETURN EXPECT(output.innerText, "1, 2, 4") + EXPECT(JSON_STRINGIFY(result), '["1","2","4"]') \ No newline at end of file diff --git a/e2e/tests/el_select_single_d.fql b/e2e/tests/el_select_single_d.fql new file mode 100644 index 00000000..8964511e --- /dev/null +++ b/e2e/tests/el_select_single_d.fql @@ -0,0 +1,10 @@ +LET url = @dynamic + "?redirect=/forms" +LET doc = DOCUMENT(url, true) + +WAIT_ELEMENT(doc, "form") + +LET input = ELEMENT(doc, "#select_input") +LET output = ELEMENT(doc, "#select_output") +LET result = SELECT(input, ["4"]) + +RETURN EXPECT(output.innerText, "4") + EXPECT(JSON_STRINGIFY(result), '["4"]') \ No newline at end of file diff --git a/e2e/tests/inner_html_dynamic.fql b/e2e/tests/inner_html_dynamic.fql deleted file mode 100644 index c1041448..00000000 --- a/e2e/tests/inner_html_dynamic.fql +++ /dev/null @@ -1,10 +0,0 @@ -// LET url = @server + '/bootstrap/overview.html' -// LET doc = DOCUMENT(url, true) -// LET selector = '.section-nav' - -// LET expected = '
  • Containers
  • Responsive breakpoints
  • Z-index
  • ' -// LET actual = INNER_HTML(doc, selector) - -// RETURN EXPECT(REGEXP_REPLACE(expected, '\s', ''), REGEXP_REPLACE(TRIM(actual), '(\n|\s)', '')) - -RETURN "" \ No newline at end of file diff --git a/e2e/tests/inner_text_dynamic.fql b/e2e/tests/inner_text_dynamic.fql deleted file mode 100644 index a2b90393..00000000 --- a/e2e/tests/inner_text_dynamic.fql +++ /dev/null @@ -1,9 +0,0 @@ -// LET url = @server + '/bootstrap/overview.html' -// LET doc = DOCUMENT(url, true) -// LET selector = 'body > div > div > main > p.bd-lead' - -// LET expected = "Components and options for laying out your Bootstrap project, including wrapping containers, a powerful grid system, a flexible media object, and responsive utility classes." -// LET actual = INNER_TEXT(doc, selector) - -// RETURN EXPECT(expected, actual) -RETURN "" \ No newline at end of file diff --git a/e2e/tests/page-load.fql b/e2e/tests/page_load.fql similarity index 52% rename from e2e/tests/page-load.fql rename to e2e/tests/page_load.fql index f7fdab18..302887e3 100644 --- a/e2e/tests/page-load.fql +++ b/e2e/tests/page_load.fql @@ -1,4 +1,4 @@ -LET url = @server + '/bootstrap/overview.html' +LET url = @static + '/overview.html' LET doc = DOCUMENT(url) RETURN EXPECT(doc.url, url) \ No newline at end of file diff --git a/e2e/tests/page_load_d.fql b/e2e/tests/page_load_d.fql new file mode 100644 index 00000000..f4fedabc --- /dev/null +++ b/e2e/tests/page_load_d.fql @@ -0,0 +1,4 @@ +LET url = @dynamic +LET doc = DOCUMENT(url, true) + +RETURN EXPECT(doc.url, url) \ No newline at end of file diff --git a/pkg/html/dynamic/document.go b/pkg/html/dynamic/document.go index 6e8a1ef8..30c1c1e5 100644 --- a/pkg/html/dynamic/document.go +++ b/pkg/html/dynamic/document.go @@ -356,7 +356,7 @@ func (doc *HTMLDocument) InnerTextBySelector(selector values.String) values.Stri doc.Lock() defer doc.Unlock() - return doc.element.InnerHTMLBySelector(selector) + return doc.element.InnerTextBySelector(selector) } func (doc *HTMLDocument) InnerTextBySelectorAll(selector values.String) *values.Array { @@ -479,6 +479,58 @@ func (doc *HTMLDocument) InputBySelector(selector values.String, value core.Valu return values.True, nil } +func (doc *HTMLDocument) SelectBySelector(selector values.String, value *values.Array) (*values.Array, error) { + res, err := eval.Eval( + doc.client, + fmt.Sprintf(` + var element = document.querySelector(%s); + + if (element == null) { + return []; + } + + var values = %s; + + if (element.nodeName.toLowerCase() !== 'select') { + throw new Error('Element is not a element.") + } + + id, err := uuid.NewV4() + + if err != nil { + return nil, err + } + + ctx, cancel := contextWithTimeout() + defer cancel() + + err = el.client.DOM.SetAttributeValue(ctx, dom.NewSetAttributeValueArgs(el.id.nodeID, attrID, id.String())) + + if err != nil { + return nil, err + } + + res, err := eval.Eval( + el.client, + fmt.Sprintf(` + var element = document.querySelector('[%s="%s"]'); + + if (element == null) { + return []; + } + + var values = %s; + + if (element.nodeName.toLowerCase() !== 'select') { + throw new Error('Element is not a