Skip to content

Commit

Permalink
Feature/MontFerret#577 spa routing (MontFerret#584)
Browse files Browse the repository at this point in the history
* Added support of getting URL dynamically
  • Loading branch information
ziflex authored and bundleman committed Apr 5, 2021
1 parent e119a36 commit 0902ddf
Show file tree
Hide file tree
Showing 23 changed files with 66 additions and 198 deletions.
8 changes: 4 additions & 4 deletions e2e/pages/dynamic/components/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ export default function Layout({ children }) {
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(NavLink, { className: "nav-link nav-link-forms", to: "/forms" }, "Forms")
]),
e("li", { className: "nav-item"}, [
e(NavLink, { className: "nav-link", to: "/navigation" }, "Navigation")
e(NavLink, { className: "nav-link nav-link-navigation", to: "/navigation" }, "Navigation")
]),
e("li", { className: "nav-item"}, [
e(NavLink, { className: "nav-link", to: "/events" }, "Events")
e(NavLink, { className: "nav-link nav-link-events", to: "/events" }, "Events")
]),
e("li", { className: "nav-item"}, [
e(NavLink, { className: "nav-link", to: "/iframe" }, "iFrame")
e(NavLink, { className: "nav-link nav-link-iframe", to: "/iframe" }, "iFrame")
])
])
])
Expand Down
6 changes: 2 additions & 4 deletions e2e/tests/dynamic/doc/inner_html/get.fql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
LET url = @lab.cdn.dynamic
LET doc = DOCUMENT(url, true)
LET doc = DOCUMENT(url, { driver: "cdp" })

LET expected = `<head>
<meta charset="utf-8">
Expand All @@ -11,16 +11,14 @@ LET expected = `<head>
<link rel="stylesheet" href="index.css">
</head>
<body class="text-center">
<div id="root"><div id="layout"><nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4" id="navbar"><a class="navbar-brand active" aria-current="page" href="#/">Ferret</a><button class="navbar-toggler" type="button"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="#/forms">Forms</a></li><li class="nav-item"><a class="nav-link" href="#/navigation">Navigation</a></li><li class="nav-item"><a class="nav-link" href="#/events">Events</a></li><li class="nav-item"><a class="nav-link" href="#/iframe">iFrame</a></li></ul></div></nav><main class="container"><div class="jumbotron" data-type="page" id="index"><div><h1>Welcome to Ferret E2E test page!</h1></div><div><p class="lead">It has several pages for testing different possibilities of the library</p></div></div></main></div></div>
<div id="root"><div id="layout"><nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4" id="navbar"><a class="navbar-brand active" aria-current="page" href="#/">Ferret</a><button class="navbar-toggler" type="button"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link nav-link-forms" href="#/forms">Forms</a></li><li class="nav-item"><a class="nav-link nav-link-navigation" href="#/navigation">Navigation</a></li><li class="nav-item"><a class="nav-link nav-link-events" href="#/events">Events</a></li><li class="nav-item"><a class="nav-link nav-link-iframe" href="#/iframe">iFrame</a></li></ul></div></nav><main class="container"><div class="jumbotron" data-type="page" id="index"><div><h1>Welcome to Ferret E2E test page!</h1></div><div><p class="lead">It has several pages for testing different possibilities of the library</p></div></div></main></div></div>
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/history.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-router.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-router-dom.js"></script>
<script src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js" crossorigin=""> </script>
<script src="index.js" type="module"></script>


</body>`
LET actual = INNER_HTML(doc)

Expand Down
4 changes: 2 additions & 2 deletions e2e/tests/dynamic/element/inner_html/get.fql
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
LET url = @lab.cdn.dynamic
LET doc = DOCUMENT(url, true)
LET doc = DOCUMENT(url, { driver: "cdp" })
LET el = ELEMENT(doc, "#root")

LET expected = `<div id="layout"><nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4" id="navbar"><a class="navbar-brand active" aria-current="page" href="#/">Ferret</a><button class="navbar-toggler" type="button"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="#/forms">Forms</a></li><li class="nav-item"><a class="nav-link" href="#/navigation">Navigation</a></li><li class="nav-item"><a class="nav-link" href="#/events">Events</a></li><li class="nav-item"><a class="nav-link" href="#/iframe">iFrame</a></li></ul></div></nav><main class="container"><div class="jumbotron" data-type="page" id="index"><div><h1>Welcome to Ferret E2E test page!</h1></div><div><p class="lead">It has several pages for testing different possibilities of the library</p></div></div></main></div>`
LET expected = `<div id="layout"><nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4" id="navbar"><a class="navbar-brand active" aria-current="page" href="#/">Ferret</a><button class="navbar-toggler" type="button"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link nav-link-forms" href="#/forms">Forms</a></li><li class="nav-item"><a class="nav-link nav-link-navigation" href="#/navigation">Navigation</a></li><li class="nav-item"><a class="nav-link nav-link-events" href="#/events">Events</a></li><li class="nav-item"><a class="nav-link nav-link-iframe" href="#/iframe">iFrame</a></li></ul></div></nav><main class="container"><div class="jumbotron" data-type="page" id="index"><div><h1>Welcome to Ferret E2E test page!</h1></div><div><p class="lead">It has several pages for testing different possibilities of the library</p></div></div></main></div>`
LET actual = INNER_HTML(el)

LET r1 = '(\s|\")'
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
LET url = @lab.cdn.dynamic
LET doc = DOCUMENT(url, { driver: "cdp" })

LET expected = url + '/'
LET expected = url + '/#/'
LET actual = doc.url

T::EQ(actual, expected)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ LET p = DOCUMENT("", { driver: "cdp" })

NAVIGATE(p, url)

RETURN T::EQ(p.url, url + '/')
RETURN T::EQ(p.url, url + '/#/')
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions e2e/tests/dynamic/page/url/get.fql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
LET page = DOCUMENT(@lab.cdn.dynamic, {
driver: 'cdp'
})

LET initialDoc = page.frames[0].url
LET initial = page.url

CLICK(page, ".nav-link-forms")

LET currentDoc = page.frames[0].url
LET current = page.url

T::NOT::EQ(initial, current)
T::EQ(initialDoc, currentDoc)

RETURN NONE
4 changes: 4 additions & 0 deletions pkg/drivers/cdp/dom/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,10 @@ func (doc *HTMLDocument) ScrollByXY(ctx context.Context, x, y values.Float, opti
return doc.input.ScrollByXY(ctx, float64(x), float64(y), options)
}

func (doc *HTMLDocument) Eval(ctx context.Context, expression string) (core.Value, error) {
return doc.exec.EvalWithReturnValue(ctx, expression)
}

func (doc *HTMLDocument) logError(err error) *zerolog.Event {
return doc.logger.
Error().
Expand Down
176 changes: 0 additions & 176 deletions pkg/drivers/cdp/dom/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,169 +2,51 @@ package dom

import (
"context"
"io"
"sync"

"github.com/mafredri/cdp"
"github.com/mafredri/cdp/protocol/dom"
"github.com/mafredri/cdp/protocol/page"
"github.com/mafredri/cdp/rpcc"
"github.com/pkg/errors"
"github.com/rs/zerolog"

"github.com/MontFerret/ferret/pkg/drivers/cdp/events"
"github.com/MontFerret/ferret/pkg/drivers/cdp/input"
"github.com/MontFerret/ferret/pkg/drivers/common"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)

var (
eventDocumentUpdated = events.New("doc_updated")
eventChildNodeInserted = events.New("child_inserted")
eventChildNodeRemoved = events.New("child_removed")
)

type (
DocumentUpdatedListener func(ctx context.Context)

AttrModifiedListener func(ctx context.Context, nodeID dom.NodeID, name, value string)

AttrRemovedListener func(ctx context.Context, nodeID dom.NodeID, name string)

ChildNodeCountUpdatedListener func(ctx context.Context, nodeID dom.NodeID, count int)

ChildNodeInsertedListener func(ctx context.Context, nodeID, previousNodeID dom.NodeID, node dom.Node)

ChildNodeRemovedListener func(ctx context.Context, nodeID, previousNodeID dom.NodeID)

Manager struct {
mu sync.RWMutex
logger *zerolog.Logger
client *cdp.Client
events *events.Loop
mouse *input.Mouse
keyboard *input.Keyboard
mainFrame *AtomicFrameID
frames *AtomicFrameCollection
cancel context.CancelFunc
}
)

// a dirty workaround to let pass the vet test
func createContext() (context.Context, context.CancelFunc) {
return context.WithCancel(context.Background())
}

func New(
logger *zerolog.Logger,
client *cdp.Client,
mouse *input.Mouse,
keyboard *input.Keyboard,
) (manager *Manager, err error) {
ctx, cancel := createContext()

closers := make([]io.Closer, 0, 10)

defer func() {
if err != nil {
common.CloseAll(logger, closers, "failed to close a DOM event stream")
}
}()

onContentReady, err := client.Page.DOMContentEventFired(ctx)

if err != nil {
return nil, err
}

closers = append(closers, onContentReady)

onDocUpdated, err := client.DOM.DocumentUpdated(ctx)

if err != nil {
return nil, err
}

closers = append(closers, onDocUpdated)

onAttrModified, err := client.DOM.AttributeModified(ctx)

if err != nil {
return nil, err
}

closers = append(closers, onAttrModified)

onAttrRemoved, err := client.DOM.AttributeRemoved(ctx)

if err != nil {
return nil, err
}

closers = append(closers, onAttrRemoved)

onChildCountUpdated, err := client.DOM.ChildNodeCountUpdated(ctx)

if err != nil {
return nil, err
}

closers = append(closers, onChildCountUpdated)

onChildNodeInserted, err := client.DOM.ChildNodeInserted(ctx)

if err != nil {
return nil, err
}

closers = append(closers, onChildNodeInserted)

onChildNodeRemoved, err := client.DOM.ChildNodeRemoved(ctx)

if err != nil {
return nil, err
}

closers = append(closers, onChildNodeRemoved)

eventLoop := events.NewLoop()

eventLoop.AddSource(events.NewSource(eventDocumentUpdated, onDocUpdated, func(stream rpcc.Stream) (i interface{}, e error) {
return stream.(dom.DocumentUpdatedClient).Recv()
}))

eventLoop.AddSource(events.NewSource(eventChildNodeInserted, onChildNodeInserted, func(stream rpcc.Stream) (i interface{}, e error) {
return stream.(dom.ChildNodeInsertedClient).Recv()
}))

eventLoop.AddSource(events.NewSource(eventChildNodeRemoved, onChildNodeRemoved, func(stream rpcc.Stream) (i interface{}, e error) {
return stream.(dom.ChildNodeRemovedClient).Recv()
}))

manager = new(Manager)
manager.logger = logger
manager.client = client
manager.events = eventLoop
manager.mouse = mouse
manager.keyboard = keyboard
manager.mainFrame = NewAtomicFrameID()
manager.frames = NewAtomicFrameCollection()
manager.cancel = cancel

eventLoop.Run(ctx)

return manager, nil
}

func (m *Manager) Close() error {
errs := make([]error, 0, m.frames.Length()+1)

if m.cancel != nil {
m.cancel()
m.cancel = nil
}

m.frames.ForEach(func(f Frame, key page.FrameID) bool {
// if initialized
if f.node != nil {
Expand Down Expand Up @@ -295,64 +177,6 @@ func (m *Manager) GetFrameNodes(ctx context.Context) (*values.Array, error) {
return arr, nil
}

func (m *Manager) AddDocumentUpdatedListener(listener DocumentUpdatedListener) events.ListenerID {
m.mu.RLock()
defer m.mu.RUnlock()

return m.events.AddListener(eventDocumentUpdated, func(ctx context.Context, _ interface{}) bool {
listener(ctx)

return true
})
}

func (m *Manager) RemoveReloadListener(listenerID events.ListenerID) {
m.mu.RLock()
defer m.mu.RUnlock()

m.events.RemoveListener(eventDocumentUpdated, listenerID)
}

func (m *Manager) AddChildNodeInsertedListener(listener ChildNodeInsertedListener) events.ListenerID {
m.mu.RLock()
defer m.mu.RUnlock()

return m.events.AddListener(eventChildNodeInserted, func(ctx context.Context, message interface{}) bool {
reply := message.(*dom.ChildNodeInsertedReply)

listener(ctx, reply.ParentNodeID, reply.PreviousNodeID, reply.Node)

return true
})
}

func (m *Manager) RemoveChildNodeInsertedListener(listenerID events.ListenerID) {
m.mu.RLock()
defer m.mu.RUnlock()

m.events.RemoveListener(eventChildNodeInserted, listenerID)
}

func (m *Manager) AddChildNodeRemovedListener(listener ChildNodeRemovedListener) events.ListenerID {
m.mu.RLock()
defer m.mu.RUnlock()

return m.events.AddListener(eventChildNodeRemoved, func(ctx context.Context, message interface{}) bool {
reply := message.(*dom.ChildNodeRemovedReply)

listener(ctx, reply.ParentNodeID, reply.NodeID)

return true
})
}

func (m *Manager) RemoveChildNodeRemovedListener(listenerID events.ListenerID) {
m.mu.RLock()
defer m.mu.RUnlock()

m.events.RemoveListener(eventChildNodeRemoved, listenerID)
}

func (m *Manager) addFrameInternal(frame page.FrameTree) {
m.frames.Set(frame.Frame.ID, Frame{
tree: frame,
Expand Down
Loading

0 comments on commit 0902ddf

Please sign in to comment.