Skip to content

Commit

Permalink
Always go through the adapter to decide how to propose visits (#1054)
Browse files Browse the repository at this point in the history
* Always go through the adapter to decide whether to start a visit or update the window.location for external urls. This allows the turbo-ios and turbo-android adapters to see the proposed visits and handle them appropriately.

* Add unit tests for the native adapter interface, so we can ensure that the proper methods are called for the native adapters

* Remove unused vars
  • Loading branch information
jayohms authored Oct 31, 2023
1 parent 68bec03 commit 725fd51
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 7 deletions.
12 changes: 6 additions & 6 deletions src/core/drive/navigator.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getVisitAction } from "../../util"
import { FormSubmission } from "./form_submission"
import { expandURL, getAnchor, getRequestURL, locationIsVisitable } from "../url"
import { expandURL, getAnchor, getRequestURL } from "../url"
import { Visit } from "./visit"
import { PageSnapshot } from "./page_snapshot"

Expand All @@ -11,11 +11,7 @@ export class Navigator {

proposeVisit(location, options = {}) {
if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
this.delegate.visitProposedToLocation(location, options)
} else {
window.location.href = location.toString()
}
this.delegate.visitProposedToLocation(location, options)
}
}

Expand Down Expand Up @@ -55,6 +51,10 @@ export class Navigator {
return this.delegate.view
}

get rootLocation() {
return this.view.snapshot.rootLocation
}

get history() {
return this.delegate.history
}
Expand Down
7 changes: 6 additions & 1 deletion src/core/native/browser_adapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ProgressBar } from "../drive/progress_bar"
import { SystemStatusCode } from "../drive/visit"
import { uuid, dispatch } from "../../util"
import { locationIsVisitable } from "../url"

export class BrowserAdapter {
progressBar = new ProgressBar()
Expand All @@ -10,7 +11,11 @@ export class BrowserAdapter {
}

visitProposedToLocation(location, options) {
this.navigator.startVisit(location, options?.restorationIdentifier || uuid(), options)
if (locationIsVisitable(location, this.navigator.rootLocation)) {
this.navigator.startVisit(location, options?.restorationIdentifier || uuid(), options)
} else {
window.location.href = location.toString()
}
}

visitStarted(visit) {
Expand Down
197 changes: 197 additions & 0 deletions src/tests/unit/native_adapter_support_tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import * as Turbo from "../../index"
import { assert } from "@open-wc/testing"

class NativeAdapterSupportTest {
proposedVisits = []
startedVisits = []
completedVisits = []
startedVisitRequests = []
completedVisitRequests = []
failedVisitRequests = []
finishedVisitRequests = []
startedFormSubmissions = []
finishedFormSubmissions = []

// Adapter interface

visitProposedToLocation(location, options) {
this.proposedVisits.push({ location, options })
}

visitStarted(visit) {
this.startedVisits.push(visit)
}

visitCompleted(visit) {
this.completedVisits.push(visit)
}

visitRequestStarted(visit) {
this.startedVisitRequests.push(visit)
}

visitRequestCompleted(visit) {
this.completedVisitRequests.push(visit)
}

visitRequestFailedWithStatusCode(visit, _statusCode) {
this.failedVisitRequests.push(visit)
}

visitRequestFinished(visit) {
this.finishedVisitRequests.push(visit)
}

visitRendered(_visit) {}

formSubmissionStarted(formSubmission) {
this.startedFormSubmissions.push(formSubmission)
}

formSubmissionFinished(formSubmission) {
this.finishedFormSubmissions.push(formSubmission)
}

pageInvalidated() {}
}

let adapter

setup(() => {
adapter = new NativeAdapterSupportTest()
Turbo.registerAdapter(adapter)
})

test("test navigator adapter is native adapter", async () => {
assert.equal(adapter, Turbo.navigator.adapter)
})

test("test visit proposal location is proposed to adapter", async () => {
const url = new URL(window.location.toString())

Turbo.navigator.proposeVisit(url)
assert.equal(adapter.proposedVisits.length, 1)

const [visit] = adapter.proposedVisits
assert.equal(visit.location, url)
})

test("test visit proposal external location is proposed to adapter", async () => {
const url = new URL("https://example.com/")

Turbo.navigator.proposeVisit(url)
assert.equal(adapter.proposedVisits.length, 1)

const [visit] = adapter.proposedVisits
assert.equal(visit.location, url)
})

test("test visit started notifies adapter", async () => {
const locatable = window.location.toString()

Turbo.navigator.startVisit(locatable)
assert.equal(adapter.startedVisits.length, 1)

const [visit] = adapter.startedVisits
assert.equal(visit.location, locatable)
})

test("test visit completed notifies adapter", async () => {
const locatable = window.location.toString()

Turbo.navigator.startVisit(locatable)

const [startedVisit] = adapter.startedVisits
startedVisit.complete()

const [completedVisit] = adapter.completedVisits
assert.equal(completedVisit.location, locatable)
})

test("test visit request started notifies adapter", async () => {
const locatable = window.location.toString()

Turbo.navigator.startVisit(locatable)

const [startedVisit] = adapter.startedVisits
startedVisit.startRequest()
assert.equal(adapter.startedVisitRequests.length, 1)

const [startedVisitRequest] = adapter.startedVisitRequests
assert.equal(startedVisitRequest.location, locatable)
})

test("test visit request completed notifies adapter", async () => {
const locatable = window.location.toString()

Turbo.navigator.startVisit(locatable)

const [startedVisit] = adapter.startedVisits
startedVisit.recordResponse({ statusCode: 200, responseHTML: "responseHtml", redirected: false })
assert.equal(adapter.completedVisitRequests.length, 1)

const [completedVisitRequest] = adapter.completedVisitRequests
assert.equal(completedVisitRequest.location, locatable)
})

test("test visit request failed notifies adapter", async () => {
const locatable = window.location.toString()

Turbo.navigator.startVisit(locatable)

const [startedVisit] = adapter.startedVisits
startedVisit.recordResponse({ statusCode: 404, responseHTML: "responseHtml", redirected: false })
assert.equal(adapter.failedVisitRequests.length, 1)

const [failedVisitRequest] = adapter.failedVisitRequests
assert.equal(failedVisitRequest.location, locatable)
})

test("test visit request finished notifies adapter", async () => {
const locatable = window.location.toString()

Turbo.navigator.startVisit(locatable)

const [startedVisit] = adapter.startedVisits
startedVisit.finishRequest()
assert.equal(adapter.finishedVisitRequests.length, 1)

const [finishedVisitRequest] = adapter.finishedVisitRequests
assert.equal(finishedVisitRequest.location, locatable)
})

test("test form submission started notifies adapter", async () => {
Turbo.navigator.formSubmissionStarted("formSubmissionStub")
assert.equal(adapter.startedFormSubmissions.length, 1)

const [startedFormSubmission] = adapter.startedFormSubmissions
assert.equal(startedFormSubmission, "formSubmissionStub")
})

test("test form submission finished notifies adapter", async () => {
Turbo.navigator.formSubmissionFinished("formSubmissionStub")
assert.equal(adapter.finishedFormSubmissions.length, 1)

const [finishedFormSubmission] = adapter.finishedFormSubmissions
assert.equal(finishedFormSubmission, "formSubmissionStub")
})


test("test visit follows redirect and proposes replace visit to adapter", async () => {
const locatable = window.location.toString()
const redirectedLocation = "https://example.com"

Turbo.navigator.startVisit(locatable)

const [startedVisit] = adapter.startedVisits
startedVisit.redirectedToLocation = redirectedLocation
startedVisit.recordResponse({ statusCode: 200, responseHTML: "responseHtml", redirected: true })
startedVisit.complete()

assert.equal(adapter.completedVisitRequests.length, 1)
assert.equal(adapter.proposedVisits.length, 1)

const [visit] = adapter.proposedVisits
assert.equal(visit.location, redirectedLocation)
assert.equal(visit.options.action, "replace")
})

0 comments on commit 725fd51

Please sign in to comment.