├── .gitignore
├── tests
├── preventdefault
│ └── index.html
├── set
│ └── index.html
├── popstate
│ └── index.html
├── trailing
│ └── index.html
├── hash
│ └── index.html
├── uichange
│ └── index.html
├── replace
│ └── index.html
├── uri
│ └── index.html
└── index.js
├── .travis.yml
├── webpack.config.js
├── LICENSE
├── package.json
├── README.md
├── index.js
└── CONTRIBUTING.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 |
--------------------------------------------------------------------------------
/tests/preventdefault/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/tests/set/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 | notifications:
7 | email: false
8 | node_js:
9 | - '4'
10 | before_install:
11 | - npm i -g npm@^2.0.0
12 | before_script:
13 | - npm prune
14 | - npm start&
15 | script:
16 | - npm test
17 | after_success:
18 | - npm run semantic-release
19 | branches:
20 | except:
21 | - "/^v\\d+\\.\\d+\\.\\d+$/"
22 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var buildPath = path.resolve(__dirname, 'build')
3 |
4 | var config = {
5 | context: __dirname,
6 | devtool: 'eval-source-map',
7 | entry: [
8 | path.resolve(__dirname, 'index.js')],
9 | output: {
10 | path: buildPath,
11 | filename: 'addressbar.js',
12 | libraryTarget: 'umd',
13 | library: 'addressbar'
14 | }
15 | }
16 |
17 | module.exports = config
18 |
--------------------------------------------------------------------------------
/tests/popstate/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Messages
6 | Message 123
7 |
8 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/trailing/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/tests/hash/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Messages
6 | Message 123
7 |
8 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/tests/uichange/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 | Home
12 | Messages
13 |
14 |
15 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/tests/replace/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
16 |
17 |
18 |
19 |
20 |
21 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Christian Alfoni
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/tests/uri/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | messages
12 |
13 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "addressbar",
3 | "version": "0.0.0-semantically-released",
4 | "description": "Makes the addressbar of the browser work just like a normal input",
5 | "main": "index.js",
6 | "scripts": {
7 | "prestart": "npm run build",
8 | "start": "python -m SimpleHTTPServer 3001",
9 | "pretest": "npm run lint && npm run build",
10 | "test": "nodeunit tests/index.js",
11 | "build": "webpack",
12 | "lint": "standard",
13 | "semantic-release": "semantic-release pre && npm publish && semantic-release post"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/cerebral/addressbar.git"
18 | },
19 | "keywords": [
20 | "router",
21 | "history",
22 | "url",
23 | "reactive"
24 | ],
25 | "author": "Christian Alfoni ",
26 | "contributors": [
27 | "Aleksey Guryanov "
28 | ],
29 | "license": "MIT",
30 | "bugs": {
31 | "url": "https://github.com/cerebral/addressbar/issues"
32 | },
33 | "homepage": "https://github.com/cerebral/addressbar#readme",
34 | "devDependencies": {
35 | "commitizen": "^2.5.0",
36 | "cz-customizable": "^2.7.0",
37 | "ghooks": "^1.0.3",
38 | "nodeunit": "^0.9.1",
39 | "selenium-webdriver": "^2.46.1",
40 | "semantic-release": "^4.3.5",
41 | "standard": "^5.4.1",
42 | "validate-commit-msg": "^1.0.0",
43 | "webpack": "^1.12.1"
44 | },
45 | "dependencies": {
46 | "eventemitter3": "^2.0.2",
47 | "url-parse": "^1.0.5"
48 | },
49 | "config": {
50 | "commitizen": {
51 | "path": "node_modules/cz-customizable"
52 | },
53 | "ghooks": {
54 | "commit-msg": "validate-commit-msg"
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # addressbar
2 | Makes the addressbar of the browser work just like a normal input
3 |
4 | [![NPM version][npm-image]][npm-url]
5 | [![Build status][travis-image]][travis-url]
6 | [![Commitizen friendly][commitizen-image]][commitizen-url]
7 | [![Semantic Release][semantic-release-image]][semantic-release-url]
8 | [![js-standard-style][standard-image]][standard-url]
9 |
10 | ## What is this thing?
11 | How would you handle URLs if the addressbar was just an input? An input you could listen to changes, `preventDefault()` on and manually set the value without any sideeffect? What if you could think about changing the url as an event in your app, which you reacted to, instead of letting a route library swallow your view layer and mess around with it in a strongly opinionated way? What if you could have the freedom to make the URL mean whatever you wanted? Not just changes in what views to display?
12 |
13 | The library just exposes the `addressbar`. It is a single entity in your app where you can:
14 |
15 | ```js
16 | // At http://www.example.com
17 |
18 | addressbar.value // "http://www.example.com"
19 |
20 | // Change addressbar value does NOT trigger route change
21 | addressbar.value = "http://wwww.example.com/test";
22 |
23 | // You can force a replace of the url setting an object as value
24 | addressbar.value = {
25 | value: "http://www.example.com/test",
26 | replace: true
27 | };
28 |
29 | // You have access to location properties
30 | addressbar.origin // "http://www.example.com"
31 | addressbar.port // ""
32 | addressbar.protocol // "http:"
33 | addressbar.hostname // "www.example.com"
34 | addressbar.pathname // "/"
35 | addressbar.hash // ""
36 |
37 | // Prevent route changes on hyperlinks
38 | addressbar.addEventListener('change', function (event) {
39 | event.preventDefault();
40 | event.target.value // The value of the addressbar
41 | });
42 | ```
43 |
44 | This is low level code, so there is no routing logic here. Please check out [url-mapper](https://github.com/christianalfoni/url-mapper) which can be used to create routing logic.
45 |
46 | ## Under the hood
47 | Addressbar listens to `popstate` events and handles hyperlinks. It basically has logic to simulate how an input works, also handling a few edge cases.
48 |
49 | ## Tests
50 | Addressbar is running with selenium-driver and nodeunit to test live in Chrome. Requires [selenium chrome driver](https://sites.google.com/a/chromium.org/chromedriver/downloads) to be installed and added to **PATH**.
51 |
52 | Run tests:
53 | - `npm install`
54 | - `npm start` (fires up a python webservice)
55 | - `npm test` (Runs tests)
56 |
57 | [npm-image]: https://img.shields.io/npm/v/addressbar.svg?style=flat
58 | [npm-url]: https://npmjs.org/package/addressbar
59 | [travis-image]: https://img.shields.io/travis/cerebral/addressbar.svg?style=flat
60 | [travis-url]: https://travis-ci.org/cerebral/addressbar
61 | [commitizen-image]: https://img.shields.io/badge/commitizen-friendly-brightgreen.svg
62 | [commitizen-url]: http://commitizen.github.io/cz-cli/
63 | [semantic-release-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square
64 | [semantic-release-url]: https://github.com/semantic-release/semantic-release
65 | [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg
66 | [standard-url]: http://standardjs.com/
67 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* global history */
2 |
3 | var URL = require('url-parse')
4 | var EventEmitter = require('eventemitter3').EventEmitter
5 | var instance = null
6 |
7 | // Check if IE history polyfill is added
8 | var location = window.history.location || window.location
9 |
10 | module.exports = (function () {
11 | if (instance) {
12 | return instance
13 | }
14 |
15 | var eventEmitter = new EventEmitter()
16 |
17 | eventEmitter.addEventListener = eventEmitter.addListener
18 | eventEmitter.removeEventListener = eventEmitter.removeListener
19 |
20 | var initialUrl = location.href
21 | var uri = URL(initialUrl)
22 | var origin = uri.protocol + '//' + uri.host
23 | var isPreventingDefault = false
24 | var doReplace = false
25 | var prevUrl = ''
26 | // var linkClicked = false
27 | var isEmitting = false
28 | var setSyncUrl = false
29 |
30 | var emitChange = function (url, event) {
31 | eventEmitter.emit('change', {
32 | preventDefault: function () {
33 | event && event.preventDefault()
34 | isPreventingDefault = true
35 | },
36 | target: {
37 | value: url ? origin + url : location.href
38 | }
39 | })
40 | }
41 |
42 | var onUrlChange = function (type) {
43 | return function (event) {
44 | if (location.href === prevUrl) {
45 | return
46 | }
47 |
48 | // Fixes bug where trailing slash is converted to normal url
49 | if (location.href[location.href.length - 1] === '/') {
50 | doReplace = true
51 | }
52 |
53 | isEmitting = true
54 | emitChange()
55 | isEmitting = false
56 |
57 | if (!setSyncUrl && isPreventingDefault) {
58 | history.replaceState({}, '', (prevUrl || initialUrl).replace(origin, ''))
59 | }
60 |
61 | prevUrl = location.href
62 | isPreventingDefault = false
63 | setSyncUrl = false
64 | doReplace = false
65 | }
66 | }
67 |
68 | // this hack resolves issue with safari
69 | // see issue from Page JS for reference https://github.com/visionmedia/page.js/issues/213
70 | // see also https://github.com/visionmedia/page.js/pull/240
71 | if (document.readyState !== 'complete') {
72 | // load event has not fired
73 | global.addEventListener('load', function () {
74 | setTimeout(function () {
75 | global.addEventListener('popstate', onUrlChange('pop'), false)
76 | }, 0)
77 | }, false)
78 | } else {
79 | // load event has fired
80 | global.addEventListener('popstate', onUrlChange('pop'), false)
81 | }
82 |
83 | Object.defineProperty(eventEmitter, 'value', {
84 | get: function () {
85 | return location.href
86 | },
87 | set: function (value) {
88 | if (typeof value !== 'string') {
89 | doReplace = Boolean(value.replace)
90 | value = value.value
91 | }
92 |
93 | // If emitting a change we flag that we are setting
94 | // a url based on the event being emitted
95 | if (isEmitting) {
96 | setSyncUrl = true
97 | }
98 |
99 | // Ensure full url
100 | if (value.indexOf(origin) === -1) {
101 | value = origin + value
102 | }
103 |
104 | // If it is same url, forget about it
105 | if (value === location.href) {
106 | return
107 | }
108 |
109 | // We might need to replace the url if we are fixing
110 | // for example trailing slash issue
111 | if (doReplace) {
112 | history.replaceState({}, '', value.replace(origin, ''))
113 | doReplace = false
114 | } else {
115 | history.pushState({}, '', value.replace(origin, ''))
116 | }
117 |
118 | prevUrl = location.href
119 | isPreventingDefault = false
120 | }
121 | })
122 |
123 | // expose URLUtils like API https://developer.mozilla.org/en-US/docs/Web/API/URLUtils
124 | // thanks https://github.com/cofounders/urlutils for reference
125 | Object.defineProperty(eventEmitter, 'origin', {
126 | get: function () {
127 | var uri = URL(location.href)
128 | return uri.protocol + '//' + uri.host
129 | }
130 | })
131 |
132 | Object.defineProperty(eventEmitter, 'protocol', {
133 | get: function () {
134 | return URL(location.href).protocol
135 | }
136 | })
137 |
138 | Object.defineProperty(eventEmitter, 'port', {
139 | get: function () {
140 | return URL(location.href).port
141 | }
142 | })
143 |
144 | Object.defineProperty(eventEmitter, 'hostname', {
145 | get: function () {
146 | return URL(location.href).hostname
147 | }
148 | })
149 |
150 | Object.defineProperty(eventEmitter, 'pathname', {
151 | get: function () {
152 | return URL(location.href).pathname
153 | }
154 | })
155 |
156 | Object.defineProperty(eventEmitter, 'hash', {
157 | get: function () {
158 | return URL(location.href).hash
159 | }
160 | })
161 |
162 | /*
163 | This code is from the Page JS source code. Amazing work on handling all
164 | kinds of scenarios with hyperlinks, thanks!
165 | */
166 |
167 | var isSameOrigin = function (href) {
168 | return (href && (href.indexOf(origin) === 0))
169 | }
170 |
171 | var getClickedHref = function (event) {
172 | // check which button
173 | if ((event.which === null ? event.button : event.which) !== 1) { return false }
174 |
175 | // check for modifiers
176 | if (event.metaKey || event.ctrlKey || event.shiftKey) { return false }
177 | if (event.defaultPrevented) { return false }
178 |
179 | // ensure link
180 | var element = event.target
181 | while (element && element.nodeName !== 'A') { element = element.parentNode }
182 | if (!element || element.nodeName !== 'A') { return false }
183 |
184 | // Ignore if tag has
185 | // 1. "download" attribute
186 | // 2. rel="external" attribute
187 | if (element.hasAttribute('download') || element.getAttribute('rel') === 'external') { return false }
188 |
189 | // Check for mailto: in the href
190 | var href = element.getAttribute('href')
191 | if (href && href.indexOf('mailto:') > -1) { return false }
192 |
193 | // check target
194 | if (element.target) { return false }
195 |
196 | // x-origin
197 | if (!isSameOrigin(element.href)) { return false }
198 |
199 | return href
200 | }
201 |
202 | global.addEventListener(document.ontouchstart ? 'touchstart' : 'click', function (event) {
203 | var href = getClickedHref(event)
204 | if (href) {
205 | // linkClicked = true
206 | isEmitting = true
207 | emitChange(href, event)
208 | isEmitting = false
209 | if (isPreventingDefault) {
210 | // linkClicked = false
211 | }
212 | prevUrl = href
213 | isPreventingDefault = false
214 | }
215 | })
216 |
217 | instance = eventEmitter
218 |
219 | return eventEmitter
220 | }())
221 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Cerebral
2 |
3 | Please take a moment to review this document in order to make the contribution
4 | process easy and effective for everyone involved.
5 |
6 | Following these guidelines helps to communicate that you respect the time of
7 | the developers managing and developing this open source project. In return,
8 | they should reciprocate that respect in addressing your issue, assessing
9 | changes, and helping you finalize your pull requests.
10 |
11 |
12 | ## Using the issue tracker
13 |
14 | The issue tracker is the preferred channel for [bug reports](#bugs),
15 | [features requests](#features) and [submitting pull
16 | requests](#pull-requests), but please respect the following restrictions:
17 |
18 | * Please **do not** use the issue tracker for personal support requests. Use
19 | the [Cerebral Chat](https://discord.gg/0kIweV4bd2bwwsvH).
20 |
21 | * Please **do not** derail or troll issues. Keep the discussion on topic and
22 | respect the opinions of others.
23 |
24 |
25 | ## Bug reports
26 |
27 | A bug is a _demonstrable problem_ that is caused by the code in the repository.
28 | Good bug reports are extremely helpful - thank you!
29 |
30 | Guidelines for bug reports:
31 |
32 | 1. **Use the GitHub issue search** — check if the issue has already been
33 | reported.
34 |
35 | 2. **Check if the issue has been fixed** — try to reproduce it using the
36 | latest `master` or `next` branch in the repository.
37 |
38 | 3. **Isolate the problem** — ideally create a reduced test case.
39 |
40 | A good bug report shouldn't leave others needing to chase you up for more
41 | information. Please try to be as detailed as possible in your report. What is
42 | your environment? What steps will reproduce the issue? What OS experiences the
43 | problem? What would you expect to be the outcome? All these details will help
44 | people to fix any potential bugs.
45 |
46 | Example:
47 |
48 | > Short and descriptive example bug report title
49 | >
50 | > A summary of the issue and the browser/OS environment in which it occurs. If
51 | > suitable, include the steps required to reproduce the bug.
52 | >
53 | > 1. This is the first step
54 | > 2. This is the second step
55 | > 3. Further steps, etc.
56 | >
57 | > `` - a link to the reduced test case
58 | >
59 | > Any other information you want to share that is relevant to the issue being
60 | > reported. This might include the lines of code that you have identified as
61 | > causing the bug, and potential solutions (and your opinions on their
62 | > merits).
63 |
64 |
65 | ## Feature requests
66 |
67 | Feature requests are welcome. But take a moment to find out whether your idea
68 | fits with the scope and aims of the project. It's up to *you* to make a strong
69 | case to convince the project's developers of the merits of this feature. Please
70 | provide as much detail and context as possible.
71 |
72 |
73 | ## Pull requests
74 |
75 | Good pull requests - patches, improvements, new features - are a fantastic
76 | help. They should remain focused in scope and avoid containing unrelated
77 | commits.
78 |
79 | **Please ask first** before embarking on any significant pull request (e.g.
80 | implementing features, refactoring code), otherwise you risk spending a lot of
81 | time working on something that the project's developers might not want to merge
82 | into the project.
83 |
84 | Please adhere to [JavaScript Standard Style](https://github.com/feross/standard)
85 | and any other requirements (such as test coverage).
86 |
87 | Adhering to the following this process is the best way to get your work
88 | included in the project:
89 |
90 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
91 | and configure the remotes:
92 |
93 | ```bash
94 | # Clone your fork of the repo into the current directory
95 | git clone https://github.com//
96 | # Navigate to the newly cloned directory
97 | cd
98 | # Assign the original repo to a remote called "upstream"
99 | git remote add upstream https://github.com/cerebral/
100 | ```
101 |
102 | 2. If you cloned a while ago, get the latest changes from upstream:
103 |
104 | ```bash
105 | git checkout master
106 | git pull upstream master
107 | ```
108 |
109 | 3. Create a new topic branch (off the main project development branch) to
110 | contain your feature, change, or fix:
111 |
112 | ```bash
113 | git checkout -b
114 | ```
115 |
116 | 4. Make sure to update, or add to the tests when appropriate. Patches and
117 | features will not be accepted without tests. Run `npm test` to check that
118 | all tests pass after you've made changes.
119 |
120 | 5. Commit your changes in logical chunks. Please adhere to these [git commit
121 | message guidelines][commit-message-conventions]
122 | or your code can't be merged into the main project. Use Git's
123 | [interactive rebase](https://help.github.com/articles/interactive-rebase)
124 | feature to tidy up your commits before making them public.
125 |
126 | 6. Locally merge (or rebase) the upstream development branch into your topic branch:
127 |
128 | ```bash
129 | git pull [--rebase] upstream master
130 | ```
131 |
132 | 7. Push your topic branch up to your fork:
133 |
134 | ```bash
135 | git push origin
136 | ```
137 |
138 | 8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
139 | with a clear title and description.
140 |
141 | 9. If you are asked to amend your changes before they can be merged in, please
142 | use `git commit --amend` (or rebasing for multi-commit Pull Requests) and
143 | force push to your remote feature branch. You may also be asked to squash
144 | commits to follow our commit conventions, as they are used by
145 | [semantic-release](https://github.com/semantic-release/semantic-release) to
146 | automatically determine the new version and release to npm. In a nutshell:
147 |
148 | #### Commit Message Conventions
149 |
150 | - Commit test files with `test: ...` or `test(scope): ...` prefix
151 | - Commit bug fixes with `fix: ...` or `fix(scope): ...` prefix
152 | - Commit breaking changes by adding `BREAKING CHANGE: ` in the commit body
153 | (not the subject line)
154 | - Commit changes to `package.json`, `.gitignore` and other meta files with
155 | `chore(filenamewithoutext): ...`
156 | - Commit changes to README files or comments with `docs: ...`
157 |
158 | We follow [Angular’s Commit Message Conventions][commit-message-conventions].
159 | But don’t worry about it, we are happy to help :)
160 |
161 |
162 | **IMPORTANT**: By submitting a patch, you agree to license your work under the
163 | same license as that used by the project.
164 |
171 | ## Maintainers
172 |
173 | If you have commit access, please follow this process for merging patches and cutting new releases.
174 |
175 | ### Reviewing changes
176 |
177 | 1. Check that a change is within the scope and philosophy of the component.
178 | 2. Check that a change has any necessary tests and a well formed, descriptive commit message.
179 | 3. Checkout the change and test it locally.
180 | 4. If the change is good, and authored by someone who cannot commit to
181 | `master`, please try to avoid using GitHub's merge button. Apply the change
182 | to `master` locally (feel free to amend any minor problems in the author's
183 | original commit if necessary).
184 | 5. If the change is good, and authored by another maintainer/collaborator, give
185 | them a "+1" comment and let them handle the merge.
186 |
187 | ### Submitting changes
188 |
189 | 1. All non-trivial changes should be put up for review using GitHub Pull
190 | Requests.
191 | 2. Your change should not be merged into `master` (or another feature branch),
192 | without at least one "+1" comment from another maintainer/collaborator
193 | on the project.
194 | 3. Try to avoid using GitHub's merge button. Locally rebase your change onto
195 | `master` and then push to GitHub.
196 | 4. Once a feature branch has been merged into its target branch, please delete
197 | the feature branch from the remote repository.
198 |
199 | [commit-message-conventions]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y
200 |
--------------------------------------------------------------------------------
/tests/index.js:
--------------------------------------------------------------------------------
1 | var webdriver = require('selenium-webdriver')
2 | var by = webdriver.By
3 | var baseUrl = 'http://localhost:3001/'
4 | var preventUrl = baseUrl + 'tests/preventdefault/'
5 | var setUrl = baseUrl + 'tests/set/'
6 | var popstateUrl = baseUrl + 'tests/popstate/'
7 | var hashUrl = baseUrl + 'tests/hash/'
8 | var trailingUrl = baseUrl + 'tests/trailing/'
9 | var uichange = baseUrl + 'tests/uichange/'
10 | var replace = baseUrl + 'tests/replace/'
11 | var uri = baseUrl + 'tests/uri/'
12 |
13 | exports['should display current url'] = function (test) {
14 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
15 | driver.get(preventUrl)
16 | driver.getCurrentUrl().then(function (url) {
17 | test.equal(url, preventUrl)
18 | })
19 | driver.quit().then(test.done)
20 | }
21 |
22 | exports['should not allow going to a new url'] = function (test) {
23 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
24 | driver.get(preventUrl)
25 | driver.navigate().to(preventUrl + '#/foo')
26 | driver.getCurrentUrl().then(function (url) {
27 | test.equal(url, preventUrl)
28 | })
29 | driver.quit().then(test.done)
30 | }
31 |
32 | exports['should set url manually when prevented'] = function (test) {
33 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
34 | driver.get(setUrl)
35 | driver.navigate().to(setUrl + '#/foo')
36 | driver.getCurrentUrl().then(function (url) {
37 | test.equal(url, setUrl + '#/foo')
38 | })
39 | driver.quit().then(test.done)
40 | }
41 |
42 | exports['should go to popstate url'] = function (test) {
43 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
44 | driver.get(popstateUrl)
45 |
46 | driver.findElement(by.id('messages')).click()
47 | driver.getCurrentUrl().then(function (url) {
48 | test.equal(url, baseUrl + 'messages')
49 | })
50 |
51 | driver.findElement(by.id('message')).click()
52 | driver.getCurrentUrl().then(function (url) {
53 | test.equal(url, baseUrl + 'messages/123')
54 | })
55 |
56 | driver.quit().then(test.done)
57 | }
58 |
59 | exports['should handle back and forward with popstate'] = function (test) {
60 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
61 | driver.get(popstateUrl)
62 |
63 | driver.findElement(by.id('messages')).click()
64 | driver.findElement(by.id('message')).click()
65 | driver.getCurrentUrl().then(function (url) {
66 | test.equal(url, baseUrl + 'messages/123')
67 | })
68 |
69 | driver.navigate().back()
70 | driver.getCurrentUrl().then(function (url) {
71 | test.equal(url, baseUrl + 'messages')
72 | })
73 |
74 | driver.navigate().forward()
75 | driver.getCurrentUrl().then(function (url) {
76 | test.equal(url, baseUrl + 'messages/123')
77 | })
78 |
79 | driver.quit().then(test.done)
80 | }
81 |
82 | exports['should go to hash url'] = function (test) {
83 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
84 | driver.get(hashUrl)
85 |
86 | driver.findElement(by.id('messages')).click()
87 | driver.getCurrentUrl().then(function (url) {
88 | test.equal(url, baseUrl + '#/messages')
89 | })
90 |
91 | driver.findElement(by.id('message')).click()
92 | driver.getCurrentUrl().then(function (url) {
93 | test.equal(url, baseUrl + '#/messages/123')
94 | })
95 |
96 | driver.quit().then(test.done)
97 | }
98 |
99 | exports['should handle back and forward with hash'] = function (test) {
100 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
101 | driver.get(hashUrl)
102 |
103 | driver.findElement(by.id('messages')).click()
104 | driver.findElement(by.id('message')).click()
105 | driver.getCurrentUrl().then(function (url) {
106 | test.equal(url, baseUrl + '#/messages/123')
107 | })
108 |
109 | driver.navigate().back()
110 | driver.getCurrentUrl().then(function (url) {
111 | test.equal(url, baseUrl + '#/messages')
112 | })
113 |
114 | driver.navigate().forward()
115 | driver.getCurrentUrl().then(function (url) {
116 | test.equal(url, baseUrl + '#/messages/123')
117 | })
118 |
119 | driver.quit().then(test.done)
120 | }
121 |
122 | exports['should handle trailing slash convertion'] = function (test) {
123 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
124 | driver.get(trailingUrl + '#/messages/123')
125 | driver.navigate().to(trailingUrl + '#/messages/')
126 | driver.getCurrentUrl().then(function (url) {
127 | test.equal(url, trailingUrl + '#/messages')
128 | })
129 |
130 | driver.navigate().back()
131 | driver.getCurrentUrl().then(function (url) {
132 | test.equal(url, trailingUrl + '#/messages/123')
133 | })
134 |
135 | driver.navigate().forward()
136 | driver.getCurrentUrl().then(function (url) {
137 | test.equal(url, trailingUrl + '#/messages')
138 | })
139 |
140 | driver.quit().then(test.done)
141 | }
142 |
143 | exports['should resume history when changing url when on "back" url'] = function (test) {
144 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
145 | driver.get(hashUrl)
146 |
147 | driver.findElement(by.id('messages')).click()
148 | driver.findElement(by.id('message')).click()
149 | driver.getCurrentUrl().then(function (url) {
150 | test.equal(url, baseUrl + '#/messages/123')
151 | })
152 |
153 | driver.navigate().back()
154 | driver.getCurrentUrl().then(function (url) {
155 | test.equal(url, baseUrl + '#/messages')
156 | })
157 |
158 | driver.navigate().to(baseUrl + '#/messages/456')
159 | driver.navigate().forward()
160 | driver.getCurrentUrl().then(function (url) {
161 | test.equal(url, baseUrl + '#/messages/456')
162 | })
163 |
164 | driver.quit().then(test.done)
165 | }
166 |
167 | exports['should be able to go forward and backwards twice'] = function (test) {
168 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
169 | driver.get(uichange)
170 |
171 | driver.findElement(by.id('messages')).click()
172 | driver.getCurrentUrl().then(function (url) {
173 | test.equal(url, baseUrl + '#/messages')
174 | })
175 | driver.findElement(by.id('url')).getText().then(function (text) {
176 | test.equal(text, baseUrl + '#/messages')
177 | })
178 |
179 | driver.navigate().back()
180 | driver.getCurrentUrl().then(function (url) {
181 | test.equal(url, baseUrl + 'tests/uichange/')
182 | })
183 | driver.findElement(by.id('url')).getText().then(function (text) {
184 | test.equal(text, baseUrl + 'tests/uichange/')
185 | })
186 |
187 | driver.findElement(by.id('messages')).click()
188 | driver.getCurrentUrl().then(function (url) {
189 | test.equal(url, baseUrl + '#/messages')
190 | })
191 | driver.findElement(by.id('url')).getText().then(function (text) {
192 | test.equal(text, baseUrl + '#/messages')
193 | })
194 |
195 | driver.navigate().back()
196 | driver.getCurrentUrl().then(function (url) {
197 | test.equal(url, baseUrl + 'tests/uichange/')
198 | })
199 | driver.findElement(by.id('url')).getText().then(function (text) {
200 | test.equal(text, baseUrl + 'tests/uichange/')
201 | })
202 |
203 | driver.quit().then(test.done)
204 | }
205 |
206 | exports['should be able to move back and forward with mix of url and setting manually and still use back button'] = function (test) {
207 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
208 | driver.get(uichange)
209 |
210 | driver.findElement(by.id('home')).click()
211 | driver.getCurrentUrl().then(function (url) {
212 | test.equal(url, baseUrl + '#/')
213 | })
214 | driver.findElement(by.id('url')).getText().then(function (text) {
215 | test.equal(text, baseUrl + '#/')
216 | })
217 |
218 | driver.findElement(by.id('messages')).click()
219 | driver.getCurrentUrl().then(function (url) {
220 | test.equal(url, baseUrl + '#/messages')
221 | })
222 | driver.findElement(by.id('url')).getText().then(function (text) {
223 | test.equal(text, baseUrl + '#/messages')
224 | })
225 |
226 | driver.findElement(by.id('home')).click()
227 | driver.getCurrentUrl().then(function (url) {
228 | test.equal(url, baseUrl + '#/')
229 | })
230 | driver.findElement(by.id('url')).getText().then(function (text) {
231 | test.equal(text, baseUrl + '#/')
232 | })
233 |
234 | driver.findElement(by.id('messages')).click()
235 | driver.getCurrentUrl().then(function (url) {
236 | test.equal(url, baseUrl + '#/messages')
237 | })
238 | driver.findElement(by.id('url')).getText().then(function (text) {
239 | test.equal(text, baseUrl + '#/messages')
240 | })
241 |
242 | driver.navigate().back()
243 | driver.getCurrentUrl().then(function (url) {
244 | test.equal(url, baseUrl + '#/')
245 | })
246 | driver.findElement(by.id('url')).getText().then(function (text) {
247 | test.equal(text, baseUrl + '#/')
248 | })
249 |
250 | driver.navigate().back()
251 | driver.getCurrentUrl().then(function (url) {
252 | test.equal(url, baseUrl + '#/messages')
253 | })
254 | driver.findElement(by.id('url')).getText().then(function (text) {
255 | test.equal(text, baseUrl + '#/messages')
256 | })
257 |
258 | driver.navigate().back()
259 | driver.getCurrentUrl().then(function (url) {
260 | test.equal(url, baseUrl + '#/')
261 | })
262 | driver.findElement(by.id('url')).getText().then(function (text) {
263 | test.equal(text, baseUrl + '#/')
264 | })
265 |
266 | driver.navigate().back()
267 | driver.getCurrentUrl().then(function (url) {
268 | test.equal(url, baseUrl + 'tests/uichange/')
269 | })
270 | driver.findElement(by.id('url')).getText().then(function (text) {
271 | test.equal(text, baseUrl + 'tests/uichange/')
272 | })
273 |
274 | driver.quit().then(test.done)
275 | }
276 |
277 | exports['should be able to replace the set url'] = function (test) {
278 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
279 | driver.get(replace)
280 |
281 | driver.findElement(by.id('home')).click()
282 | driver.getCurrentUrl().then(function (url) {
283 | test.equal(url, baseUrl + '#/home')
284 | })
285 |
286 | driver.findElement(by.id('messagesReplace')).click()
287 | driver.getCurrentUrl().then(function (url) {
288 | test.equal(url, baseUrl + '#/messages')
289 | })
290 |
291 | driver.navigate().back()
292 | driver.getCurrentUrl().then(function (url) {
293 | test.equal(url, 'http://localhost:3001/tests/replace/')
294 | })
295 | driver.findElement(by.id('url')).getText().then(function (text) {
296 | test.equal(text, 'http://localhost:3001/tests/replace/')
297 | })
298 |
299 | driver.findElement(by.id('home')).click()
300 | driver.getCurrentUrl().then(function (url) {
301 | test.equal(url, baseUrl + '#/home')
302 | })
303 |
304 | driver.findElement(by.id('messages')).click()
305 | driver.getCurrentUrl().then(function (url) {
306 | test.equal(url, baseUrl + '#/messages')
307 | })
308 |
309 | driver.navigate().back()
310 | driver.getCurrentUrl().then(function (url) {
311 | test.equal(url, 'http://localhost:3001/#/home')
312 | })
313 | driver.findElement(by.id('url')).getText().then(function (text) {
314 | test.equal(text, 'http://localhost:3001/#/home')
315 | })
316 |
317 | driver.quit().then(test.done)
318 | }
319 |
320 | exports['should expose origin, protocol, port and hostname as properties'] = function (test) {
321 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.phantomjs()).build()
322 | driver.get(uri)
323 |
324 | driver.getCurrentUrl().then(function (url) {
325 | test.equal(url, 'http://localhost:3001/tests/uri/')
326 | })
327 | driver.findElement(by.id('origin')).getText().then(function (text) {
328 | test.equal(text, 'http://localhost:3001')
329 | })
330 | driver.findElement(by.id('protocol')).getText().then(function (text) {
331 | test.equal(text, 'http:')
332 | })
333 | driver.findElement(by.id('port')).getText().then(function (text) {
334 | test.equal(text, '3001')
335 | })
336 | driver.findElement(by.id('hostname')).getText().then(function (text) {
337 | test.equal(text, 'localhost')
338 | })
339 | driver.findElement(by.id('pathname')).getText().then(function (text) {
340 | test.equal(text, '/tests/uri/')
341 | })
342 | driver.findElement(by.id('hash')).getText().then(function (text) {
343 | test.equal(text, '')
344 | })
345 |
346 | driver.findElement(by.id('messages')).click()
347 | driver.findElement(by.id('pathname')).getText().then(function (text) {
348 | test.equal(text, '/')
349 | })
350 | driver.findElement(by.id('hash')).getText().then(function (text) {
351 | test.equal(text, '#/messages')
352 | })
353 |
354 | driver.quit().then(test.done)
355 | }
356 |
--------------------------------------------------------------------------------