1// Copyright (C) 2018 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {Router} from './router'; 16 17const mockComponent = { 18 view() {}, 19}; 20 21describe('Router#resolve', () => { 22 beforeEach(() => { 23 window.location.hash = ''; 24 }); 25 26 test('Default route must be defined', () => { 27 expect(() => new Router({'/a': mockComponent})).toThrow(); 28 }); 29 30 test('Resolves empty route to default component', () => { 31 const router = new Router({'/': mockComponent}); 32 window.location.hash = ''; 33 expect(router.resolve().tag).toBe(mockComponent); 34 }); 35 36 test('Resolves subpage route to component of main page', () => { 37 const nonDefaultComponent = {view() {}}; 38 const router = new Router({ 39 '/': mockComponent, 40 '/a': nonDefaultComponent, 41 }); 42 window.location.hash = '#!/a/subpage'; 43 expect(router.resolve().tag).toBe(nonDefaultComponent); 44 expect(router.resolve().attrs.subpage).toBe('/subpage'); 45 }); 46 47 test('Pass empty subpage if not found in URL', () => { 48 const nonDefaultComponent = {view() {}}; 49 const router = new Router({ 50 '/': mockComponent, 51 '/a': nonDefaultComponent, 52 }); 53 window.location.hash = '#!/a'; 54 expect(router.resolve().tag).toBe(nonDefaultComponent); 55 expect(router.resolve().attrs.subpage).toBe(''); 56 }); 57}); 58 59describe('Router.parseUrl', () => { 60 // Can parse arguments from the search string. 61 test('Search parsing', () => { 62 const url = 'http://localhost?p=123&s=42&url=a?b?c'; 63 const route = Router.parseUrl(url); 64 const args = route.args; 65 expect(args.p).toBe('123'); 66 expect(args.s).toBe('42'); 67 expect(args.url).toBe('a?b?c'); 68 expect(route.fragment).toBe(''); 69 }); 70 71 // Or from the fragment string. 72 test('Fragment parsing', () => { 73 const url = 'http://localhost/#!/foo?p=123&s=42&url=a?b?c'; 74 const route = Router.parseUrl(url); 75 const args = route.args; 76 expect(args.p).toBe('123'); 77 expect(args.s).toBe('42'); 78 expect(args.url).toBe('a?b?c'); 79 expect(route.fragment).toBe(''); 80 }); 81 82 // Or both in which case fragment overrides the search. 83 test('Fragment parsing', () => { 84 const url = 85 'http://localhost/?p=1&s=2&hideSidebar=true#!/foo?s=3&url=4&hideSidebar=false'; 86 const route = Router.parseUrl(url); 87 const args = route.args; 88 expect(args.p).toBe('1'); 89 expect(args.s).toBe('3'); 90 expect(args.url).toBe('4'); 91 expect(args.hideSidebar).toBe(false); 92 expect(route.fragment).toBe(''); 93 }); 94 95 // + is also space 96 test('plus is space query', () => { 97 const url = 'http://localhost?query=(foo+%2B+bar),'; 98 const route = Router.parseUrl(url); 99 const args = route.args; 100 expect(args.query).toBe('(foo + bar),'); 101 }); 102 103 // + is also space 104 test('plus is space hash', () => { 105 const url = 'http://localhost#!/foo?query=(foo+%2B+bar),'; 106 const route = Router.parseUrl(url); 107 const args = route.args; 108 expect(args.query).toBe('(foo + bar),'); 109 }); 110 111 test('Nested fragment', () => { 112 const url = 113 'http://localhost/?p=1&s=2&hideSidebar=true#!/foo?s=3&url=4&hideSidebar=false#myfragment'; 114 const route = Router.parseUrl(url); 115 expect(route.fragment).toBe('myfragment'); 116 }); 117}); 118 119describe('Router.parseFragment', () => { 120 test('empty route broken into empty components', () => { 121 const {page, subpage, args} = Router.parseFragment(''); 122 expect(page).toBe(''); 123 expect(subpage).toBe(''); 124 expect(args.mode).toBe(undefined); 125 }); 126 127 test('by default args are undefined', () => { 128 // This prevents the url from becoming messy. 129 const {args} = Router.parseFragment(''); 130 expect(args).toEqual({}); 131 }); 132 133 test('invalid route broken into empty components', () => { 134 const {page, subpage} = Router.parseFragment('/bla'); 135 expect(page).toBe(''); 136 expect(subpage).toBe(''); 137 }); 138 139 test('simple route has page defined', () => { 140 const {page, subpage} = Router.parseFragment('#!/record'); 141 expect(page).toBe('/record'); 142 expect(subpage).toBe(''); 143 }); 144 145 test('simple route has both components defined', () => { 146 const {page, subpage} = Router.parseFragment('#!/record/memory'); 147 expect(page).toBe('/record'); 148 expect(subpage).toBe('/memory'); 149 }); 150 151 test('route broken at first slash', () => { 152 const {page, subpage} = Router.parseFragment('#!/record/memory/stuff'); 153 expect(page).toBe('/record'); 154 expect(subpage).toBe('/memory/stuff'); 155 }); 156 157 test('parameters separated from route', () => { 158 const {page, subpage, args} = Router.parseFragment( 159 '#!/record/memory?url=http://localhost:1234/aaaa', 160 ); 161 expect(page).toBe('/record'); 162 expect(subpage).toBe('/memory'); 163 expect(args.url).toEqual('http://localhost:1234/aaaa'); 164 }); 165 166 test('openFromAndroidBugTool can be false', () => { 167 const {args} = Router.parseFragment('#!/?openFromAndroidBugTool=false'); 168 expect(args.openFromAndroidBugTool).toEqual(false); 169 }); 170 171 test('openFromAndroidBugTool can be true', () => { 172 const {args} = Router.parseFragment('#!/?openFromAndroidBugTool=true'); 173 expect(args.openFromAndroidBugTool).toEqual(true); 174 }); 175 176 test('bad modes are coerced to default', () => { 177 const {args} = Router.parseFragment('#!/?mode=1234'); 178 expect(args.mode).toEqual(undefined); 179 }); 180 181 test('bad hideSidebar is coerced to default', () => { 182 const {args} = Router.parseFragment('#!/?hideSidebar=helloworld!'); 183 expect(args.hideSidebar).toEqual(undefined); 184 }); 185}); 186