github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/app.spec.tsx (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 import React from "react"; 12 import { assert } from "chai"; 13 import { Action, Store } from "redux"; 14 import { createMemoryHistory } from "history"; 15 import { mount, ReactWrapper } from "enzyme"; 16 17 import "src/enzymeInit"; 18 import { App } from "src/app"; 19 import { AdminUIState, createAdminUIStore } from "src/redux/state"; 20 21 import ClusterOverview from "src/views/cluster/containers/clusterOverview"; 22 import NodeList from "src/views/clusterviz/containers/map/nodeList"; 23 import { ClusterVisualization } from "src/views/clusterviz/containers/map"; 24 import { NodeGraphs } from "src/views/cluster/containers/nodeGraphs"; 25 import { NodeOverview } from "src/views/cluster/containers/nodeOverview"; 26 import { Logs } from "src/views/cluster/containers/nodeLogs"; 27 import { EventPageUnconnected } from "src/views/cluster/containers/events"; 28 import { JobsTable } from "src/views/jobs"; 29 import { 30 DatabaseGrantsList, 31 DatabaseTablesList, 32 } from "src/views/databases/containers/databases"; 33 import { TableMain } from "src/views/databases/containers/tableDetails"; 34 import { DataDistributionPage } from "src/views/cluster/containers/dataDistribution"; 35 import { StatementsPage } from "src/views/statements/statementsPage"; 36 import { StatementDetails } from "src/views/statements/statementDetails"; 37 import Debug from "src/views/reports/containers/debug"; 38 import { ReduxDebug } from "src/views/reports/containers/redux"; 39 import { CustomChart } from "src/views/reports/containers/customChart"; 40 import { EnqueueRange } from "src/views/reports/containers/enqueueRange"; 41 import { RangesMain } from "src/views/devtools/containers/raftRanges"; 42 import { RaftMessages } from "src/views/devtools/containers/raftMessages"; 43 import Raft from "src/views/devtools/containers/raft"; 44 import NotFound from "src/views/app/components/NotFound"; 45 import { ProblemRanges } from "src/views/reports/containers/problemRanges"; 46 import { Localities } from "src/views/reports/containers/localities"; 47 import { Nodes } from "src/views/reports/containers/nodes"; 48 import { DecommissionedNodeHistory } from "src/views/reports"; 49 import { Network } from "src/views/reports/containers/network"; 50 import { Settings } from "src/views/reports/containers/settings"; 51 import { Certificates } from "src/views/reports/containers/certificates"; 52 import { Range } from "src/views/reports/containers/range"; 53 import { Stores } from "src/views/reports/containers/stores"; 54 55 describe("Routing to", () => { 56 const history = createMemoryHistory({ 57 initialEntries: ["/"], 58 }); 59 const store: Store<AdminUIState, Action> = createAdminUIStore(history); 60 const appWrapper: ReactWrapper = mount(<App history={history} store={store}/>); 61 62 after(() => { 63 appWrapper.unmount(); 64 }); 65 66 const navigateToPath = (path: string) => { 67 history.push(path); 68 appWrapper.update(); 69 }; 70 71 describe("'/' path", () => { 72 it("routes to <ClusterOverview> component", () => { 73 navigateToPath("/"); 74 assert.lengthOf(appWrapper.find(ClusterOverview), 1); 75 }); 76 77 it("redirected to '/overview'", () => { 78 navigateToPath("/"); 79 const location = history.location; 80 assert.equal(location.pathname, "/overview/list"); 81 }); 82 }); 83 84 describe("'/overview' path", () => { 85 it("routes to <ClusterOverview> component", () => { 86 navigateToPath("/overview"); 87 assert.lengthOf(appWrapper.find(ClusterOverview), 1); 88 }); 89 90 it("redirected to '/overview'", () => { 91 navigateToPath("/overview"); 92 const location = history.location; 93 assert.equal(location.pathname, "/overview/list"); 94 }); 95 }); 96 97 describe("'/overview/list' path", () => { 98 it("routes to <NodeList> component", () => { 99 navigateToPath("/overview"); 100 const clusterOverview = appWrapper.find(ClusterOverview); 101 assert.lengthOf(clusterOverview, 1); 102 const nodeList = clusterOverview.find(NodeList); 103 assert.lengthOf(nodeList, 1); 104 }); 105 }); 106 107 describe("'/overview/map' path", () => { 108 it("routes to <ClusterViz> component", () => { 109 navigateToPath("/overview/map"); 110 const clusterOverview = appWrapper.find(ClusterOverview); 111 const clusterViz = appWrapper.find(ClusterVisualization); 112 assert.lengthOf(clusterOverview, 1); 113 assert.lengthOf(clusterViz, 1); 114 }); 115 }); 116 117 { /* time series metrics */} 118 describe("'/metrics' path", () => { 119 it("routes to <NodeGraphs> component", () => { 120 navigateToPath("/metrics"); 121 assert.lengthOf(appWrapper.find(NodeGraphs), 1); 122 }); 123 124 it("redirected to '/metrics/overview/cluster'", () => { 125 navigateToPath("/metrics"); 126 const location = history.location; 127 assert.equal(location.pathname, "/metrics/overview/cluster"); 128 }); 129 }); 130 131 describe("'/metrics/overview/cluster' path", () => { 132 it("routes to <NodeGraphs> component", () => { 133 navigateToPath("/metrics/overview/cluster"); 134 assert.lengthOf(appWrapper.find(NodeGraphs), 1); 135 }); 136 }); 137 138 describe("'/metrics/overview/node' path", () => { 139 it("routes to <NodeGraphs> component", () => { 140 navigateToPath("/metrics/overview/node"); 141 assert.lengthOf(appWrapper.find(NodeGraphs), 1); 142 }); 143 }); 144 145 describe("'/metrics/:dashboardNameAttr' path", () => { 146 it("routes to <NodeGraphs> component", () => { 147 navigateToPath("/metrics/some-dashboard"); 148 assert.lengthOf(appWrapper.find(NodeGraphs), 1); 149 }); 150 151 it("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { 152 navigateToPath("/metrics/some-dashboard"); 153 const location = history.location; 154 assert.equal(location.pathname, "/metrics/some-dashboard/cluster"); 155 }); 156 }); 157 158 describe("'/metrics/:dashboardNameAttr/cluster' path", () => { 159 it("routes to <NodeGraphs> component", () => { 160 navigateToPath("/metrics/some-dashboard/cluster"); 161 assert.lengthOf(appWrapper.find(NodeGraphs), 1); 162 }); 163 }); 164 165 describe("'/metrics/:dashboardNameAttr/node' path", () => { 166 it("routes to <NodeGraphs> component", () => { 167 navigateToPath("/metrics/some-dashboard/node"); 168 assert.lengthOf(appWrapper.find(NodeGraphs), 1); 169 }); 170 171 it("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { 172 navigateToPath("/metrics/some-dashboard/node"); 173 const location = history.location; 174 assert.equal(location.pathname, "/metrics/some-dashboard/cluster"); 175 }); 176 }); 177 178 describe("'/metrics/:dashboardNameAttr/node/:nodeIDAttr' path", () => { 179 it("routes to <NodeGraphs> component", () => { 180 navigateToPath("/metrics/some-dashboard/node/123"); 181 assert.lengthOf(appWrapper.find(NodeGraphs), 1); 182 }); 183 }); 184 185 { /* node details */} 186 describe("'/node' path", () => { 187 it("routes to <NodeList> component", () => { 188 navigateToPath("/node"); 189 assert.lengthOf(appWrapper.find(NodeList), 1); 190 }); 191 192 it("redirected to '/overview/list'", () => { 193 navigateToPath("/node"); 194 const location = history.location; 195 assert.equal(location.pathname, "/overview/list"); 196 }); 197 }); 198 199 describe("'/node/:nodeIDAttr' path", () => { 200 it("routes to <NodeOverview> component", () => { 201 navigateToPath("/node/1"); 202 assert.lengthOf(appWrapper.find(NodeOverview), 1); 203 }); 204 }); 205 206 describe("'/node/:nodeIDAttr/logs' path", () => { 207 it("routes to <Logs> component", () => { 208 navigateToPath("/node/1/logs"); 209 assert.lengthOf(appWrapper.find(Logs), 1); 210 }); 211 }); 212 213 { /* events & jobs */} 214 describe("'/events' path", () => { 215 it("routes to <EventPageUnconnected> component", () => { 216 navigateToPath("/events"); 217 assert.lengthOf(appWrapper.find(EventPageUnconnected), 1); 218 }); 219 }); 220 221 describe("'/jobs' path", () => { 222 it("routes to <JobsTable> component", () => { 223 navigateToPath("/jobs"); 224 assert.lengthOf(appWrapper.find(JobsTable), 1); 225 }); 226 }); 227 228 { /* databases */} 229 describe("'/databases' path", () => { 230 it("routes to <DatabaseTablesList> component", () => { 231 navigateToPath("/databases"); 232 assert.lengthOf(appWrapper.find(DatabaseTablesList), 1); 233 }); 234 235 it("redirected to '/databases/tables'", () => { 236 navigateToPath("/databases"); 237 const location = history.location; 238 assert.equal(location.pathname, "/databases/tables"); 239 }); 240 }); 241 242 describe("'/databases/tables' path", () => { 243 it("routes to <DatabaseTablesList> component", () => { 244 navigateToPath("/databases/tables"); 245 assert.lengthOf(appWrapper.find(DatabaseTablesList), 1); 246 }); 247 }); 248 249 describe("'/databases/grants' path", () => { 250 it("routes to <DatabaseGrantsList> component", () => { 251 navigateToPath("/databases/grants"); 252 assert.lengthOf(appWrapper.find(DatabaseGrantsList), 1); 253 }); 254 }); 255 256 describe("'/databases/database/:${databaseNameAttr}/table/:${tableNameAttr}' path", () => { 257 it("redirected to '/database/:${databaseNameAttr}/table/:${tableNameAttr}'", () => { 258 navigateToPath("/databases/database/some-db-name/table/some-table-name"); 259 const location = history.location; 260 assert.equal(location.pathname, "/database/some-db-name/table/some-table-name"); 261 }); 262 }); 263 264 describe("'/database' path", () => { 265 it("redirected to '/databases'", () => { 266 navigateToPath("/databases/tables"); 267 const location = history.location; 268 assert.equal(location.pathname, "/databases/tables"); 269 }); 270 }); 271 272 describe("'/database/:${databaseNameAttr}' path", () => { 273 it("redirected to '/databases'", () => { 274 navigateToPath("/database/some-db-name"); 275 const location = history.location; 276 assert.equal(location.pathname, "/databases/tables"); 277 }); 278 }); 279 280 describe("'/database/:${databaseNameAttr}/table' path", () => { 281 it("redirected to '/databases/tables'", () => { 282 navigateToPath("/database/some-db-name/table"); 283 const location = history.location; 284 assert.equal(location.pathname, "/databases/tables"); 285 }); 286 }); 287 288 describe("'/database/:${databaseNameAttr}/table/:${tableNameAttr}' path", () => { 289 it("routes to <TableMain> component", () => { 290 navigateToPath("/database/some-db-name/table/some-table-name"); 291 assert.lengthOf(appWrapper.find(TableMain), 1); 292 }); 293 }); 294 295 { /* data distribution */} 296 describe("'/data-distribution' path", () => { 297 it("routes to <DataDistributionPage> component", () => { 298 navigateToPath("/data-distribution"); 299 assert.lengthOf(appWrapper.find(DataDistributionPage), 1); 300 }); 301 }); 302 303 { /* statement statistics */} 304 describe("'/statements' path", () => { 305 it("routes to <StatementsPage> component", () => { 306 navigateToPath("/statements"); 307 assert.lengthOf(appWrapper.find(StatementsPage), 1); 308 }); 309 }); 310 311 describe("'/statements/:${appAttr}' path", () => { 312 it("routes to <StatementsPage> component", () => { 313 navigateToPath("/statements/(internal)"); 314 assert.lengthOf(appWrapper.find(StatementsPage), 1); 315 }); 316 }); 317 318 describe("'/statements/:${appAttr}/:${statementAttr}' path", () => { 319 it("routes to <StatementDetails> component", () => { 320 navigateToPath("/statements/(internal)/true"); 321 assert.lengthOf(appWrapper.find(StatementDetails), 1); 322 }); 323 }); 324 325 describe("'/statements/:${implicitTxnAttr}/:${statementAttr}' path", () => { 326 it("routes to <StatementDetails> component", () => { 327 navigateToPath("/statements/implicit-txn-attr/statement-attr"); 328 assert.lengthOf(appWrapper.find(StatementDetails), 1); 329 }); 330 }); 331 332 describe("'/statement' path", () => { 333 it("redirected to '/statements'", () => { 334 navigateToPath("/statement"); 335 const location = history.location; 336 assert.equal(location.pathname, "/statements"); 337 }); 338 }); 339 340 describe("'/statement/:${statementAttr}' path", () => { 341 it("routes to <StatementDetails> component", () => { 342 navigateToPath("/statement/statement-attr"); 343 assert.lengthOf(appWrapper.find(StatementDetails), 1); 344 }); 345 }); 346 347 describe("'/statement/:${implicitTxnAttr}/:${statementAttr}' path", () => { 348 it("routes to <StatementDetails> component", () => { 349 navigateToPath("/statement/implicit-attr/statement-attr/"); 350 assert.lengthOf(appWrapper.find(StatementDetails), 1); 351 }); 352 }); 353 354 { /* debug pages */} 355 describe("'/debug' path", () => { 356 it("routes to <Debug> component", () => { 357 navigateToPath("/debug"); 358 assert.lengthOf(appWrapper.find(Debug), 1); 359 }); 360 }); 361 362 // TODO (koorosh): Disabled due to strange failure on internal 363 // behavior of ReduxDebug component under test env. 364 xdescribe("'/debug/redux' path", () => { 365 it("routes to <ReduxDebug> component", () => { 366 navigateToPath("/debug/redux"); 367 assert.lengthOf(appWrapper.find(ReduxDebug), 1); 368 }); 369 }); 370 371 describe("'/debug/chart' path", () => { 372 it("routes to <CustomChart> component", () => { 373 navigateToPath("/debug/chart"); 374 // assert.lengthOf(appWrapper.find(Debug), 1); 375 assert.lengthOf(appWrapper.find(CustomChart), 1); 376 }); 377 }); 378 379 describe("'/debug/enqueue_range' path", () => { 380 it("routes to <EnqueueRange> component", () => { 381 navigateToPath("/debug/enqueue_range"); 382 assert.lengthOf(appWrapper.find(EnqueueRange), 1); 383 }); 384 }); 385 386 { /* raft pages */} 387 describe("'/raft' path", () => { 388 it("routes to <Raft> component", () => { 389 navigateToPath("/raft"); 390 assert.lengthOf(appWrapper.find(Raft), 1); 391 }); 392 393 it("redirected to '/raft/ranges'", () => { 394 navigateToPath("/raft"); 395 const location = history.location; 396 assert.equal(location.pathname, "/raft/ranges"); 397 }); 398 }); 399 400 describe("'/raft/ranges' path", () => { 401 it("routes to <RangesMain> component", () => { 402 navigateToPath("/raft/ranges"); 403 assert.lengthOf(appWrapper.find(RangesMain), 1); 404 }); 405 }); 406 407 describe("'/raft/messages/all' path", () => { 408 it("routes to <RaftMessages> component", () => { 409 navigateToPath("/raft/messages/all"); 410 assert.lengthOf(appWrapper.find(RaftMessages), 1); 411 }); 412 }); 413 414 describe("'/raft/messages/node/:${nodeIDAttr}' path", () => { 415 it("routes to <RaftMessages> component", () => { 416 navigateToPath("/raft/messages/node/node-id-attr"); 417 assert.lengthOf(appWrapper.find(RaftMessages), 1); 418 }); 419 }); 420 421 describe("'/reports/problemranges' path", () => { 422 it("routes to <ProblemRanges> component", () => { 423 navigateToPath("/reports/problemranges"); 424 assert.lengthOf(appWrapper.find(ProblemRanges), 1); 425 }); 426 }); 427 428 describe("'/reports/problemranges/:nodeIDAttr' path", () => { 429 it("routes to <ProblemRanges> component", () => { 430 navigateToPath("/reports/problemranges/1"); 431 assert.lengthOf(appWrapper.find(ProblemRanges), 1); 432 }); 433 }); 434 435 describe("'/reports/localities' path", () => { 436 it("routes to <Localities> component", () => { 437 navigateToPath("/reports/localities"); 438 assert.lengthOf(appWrapper.find(Localities), 1); 439 }); 440 }); 441 442 describe("'/reports/nodes' path", () => { 443 it("routes to <Nodes> component", () => { 444 navigateToPath("/reports/nodes"); 445 assert.lengthOf(appWrapper.find(Nodes), 1); 446 }); 447 }); 448 449 describe("'/reports/nodes/history' path", () => { 450 it("routes to <DecommissionedNodeHistory> component", () => { 451 navigateToPath("/reports/nodes/history"); 452 assert.lengthOf(appWrapper.find(DecommissionedNodeHistory), 1); 453 }); 454 }); 455 456 describe("'/reports/network' path", () => { 457 it("routes to <Network> component", () => { 458 navigateToPath("/reports/network"); 459 assert.lengthOf(appWrapper.find(Network), 1); 460 }); 461 }); 462 463 describe("'/reports/network/:nodeIDAttr' path", () => { 464 it("routes to <Network> component", () => { 465 navigateToPath("/reports/network/1"); 466 assert.lengthOf(appWrapper.find(Network), 1); 467 }); 468 }); 469 470 describe("'/reports/settings' path", () => { 471 it("routes to <Settings> component", () => { 472 navigateToPath("/reports/settings"); 473 assert.lengthOf(appWrapper.find(Settings), 1); 474 }); 475 }); 476 477 describe("'/reports/certificates/:nodeIDAttr' path", () => { 478 it("routes to <Certificates> component", () => { 479 navigateToPath("/reports/certificates/1"); 480 assert.lengthOf(appWrapper.find(Certificates), 1); 481 }); 482 }); 483 484 describe("'/reports/range/:nodeIDAttr' path", () => { 485 it("routes to <Range> component", () => { 486 navigateToPath("/reports/range/1"); 487 assert.lengthOf(appWrapper.find(Range), 1); 488 }); 489 }); 490 491 describe("'/reports/stores/:nodeIDAttr' path", () => { 492 it("routes to <Stores> component", () => { 493 navigateToPath("/reports/stores/1"); 494 assert.lengthOf(appWrapper.find(Stores), 1); 495 }); 496 }); 497 498 { /* old route redirects */} 499 describe("'/cluster' path", () => { 500 it("redirected to '/metrics/overview/cluster'", () => { 501 navigateToPath("/cluster"); 502 const location = history.location; 503 assert.equal(location.pathname, "/metrics/overview/cluster"); 504 }); 505 }); 506 507 describe("'/cluster/all/:${dashboardNameAttr}' path", () => { 508 it("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { 509 const dashboardNameAttr = "some-dashboard-name"; 510 navigateToPath(`/cluster/all/${dashboardNameAttr}`); 511 const location = history.location; 512 assert.equal(location.pathname, `/metrics/${dashboardNameAttr}/cluster`); 513 }); 514 }); 515 516 describe("'/cluster/node/:${nodeIDAttr}/:${dashboardNameAttr}' path", () => { 517 it("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { 518 const dashboardNameAttr = "some-dashboard-name"; 519 const nodeIDAttr = 1; 520 navigateToPath(`/cluster/node/${nodeIDAttr}/${dashboardNameAttr}`); 521 const location = history.location; 522 assert.equal(location.pathname, `/metrics/${dashboardNameAttr}/node/${nodeIDAttr}`); 523 }); 524 }); 525 526 describe("'/cluster/nodes' path", () => { 527 it("redirected to '/overview/list'", () => { 528 navigateToPath("/cluster/nodes"); 529 const location = history.location; 530 assert.equal(location.pathname, "/overview/list"); 531 }); 532 }); 533 534 describe("'/cluster/nodes/:${nodeIDAttr}' path", () => { 535 it("redirected to '/node/:${nodeIDAttr}'", () => { 536 const nodeIDAttr = 1; 537 navigateToPath(`/cluster/nodes/${nodeIDAttr}`); 538 const location = history.location; 539 assert.equal(location.pathname, `/node/${nodeIDAttr}`); 540 }); 541 }); 542 543 describe("'/cluster/nodes/:${nodeIDAttr}/logs' path", () => { 544 it("redirected to '/node/:${nodeIDAttr}/logs'", () => { 545 const nodeIDAttr = 1; 546 navigateToPath(`/cluster/nodes/${nodeIDAttr}/logs`); 547 const location = history.location; 548 assert.equal(location.pathname, `/node/${nodeIDAttr}/logs`); 549 }); 550 }); 551 552 describe("'/cluster/events' path", () => { 553 it("redirected to '/events'", () => { 554 navigateToPath("/cluster/events"); 555 const location = history.location; 556 assert.equal(location.pathname, "/events"); 557 }); 558 }); 559 560 describe("'/cluster/nodes' path", () => { 561 it("redirected to '/overview/list'", () => { 562 navigateToPath("/cluster/nodes"); 563 const location = history.location; 564 assert.equal(location.pathname, "/overview/list"); 565 }); 566 }); 567 568 describe("'/unknown-url' path", () => { 569 it("routes to <NotFound> component", () => { 570 navigateToPath("/some-random-ulr"); 571 assert.lengthOf(appWrapper.find(NotFound), 1); 572 }); 573 }); 574 });