github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/web/elm/tests/DashboardTests.elm (about) 1 module DashboardTests exposing 2 ( afterSeconds 3 , all 4 , almostBlack 5 , amber 6 , apiData 7 , blue 8 , brown 9 , circularJobs 10 , darkGrey 11 , fadedGreen 12 , givenDataAndUser 13 , givenDataUnauthenticated 14 , green 15 , iconSelector 16 , job 17 , jobWithNameTransitionedAt 18 , lightGrey 19 , middleGrey 20 , orange 21 , otherJob 22 , red 23 , running 24 , userWithRoles 25 , whenOnDashboard 26 , whenOnDashboardViewingAllPipelines 27 , white 28 ) 29 30 import Application.Application as Application 31 import Assets 32 import ColorValues 33 import Common 34 exposing 35 ( defineHoverBehaviour 36 , isColorWithStripes 37 , pipelineRunningKeyframes 38 ) 39 import Concourse 40 import Concourse.BuildStatus exposing (BuildStatus(..)) 41 import Concourse.Cli as Cli 42 import Concourse.PipelineStatus exposing (PipelineStatus(..)) 43 import Data 44 import Dict 45 import Expect exposing (Expectation) 46 import Html.Attributes as Attr 47 import Http 48 import Keyboard 49 import Message.Callback as Callback 50 import Message.Effects as Effects 51 import Message.Message as Msgs 52 import Message.Subscription as Subscription exposing (Delivery(..), Interval(..)) 53 import Message.TopLevelMessage as ApplicationMsgs 54 import Routes 55 import Set 56 import Test exposing (..) 57 import Test.Html.Event as Event 58 import Test.Html.Query as Query 59 import Test.Html.Selector 60 exposing 61 ( Selector 62 , attribute 63 , class 64 , containing 65 , id 66 , style 67 , tag 68 , text 69 ) 70 import Time 71 import Url 72 73 74 almostBlack : String 75 almostBlack = 76 "#1e1d1d" 77 78 79 middleGrey : String 80 middleGrey = 81 "#3d3c3c" 82 83 84 lightGrey : String 85 lightGrey = 86 "#9b9b9b" 87 88 89 menuGrey : String 90 menuGrey = 91 "#868585" 92 93 94 green : String 95 green = 96 "#11c560" 97 98 99 blue : String 100 blue = 101 "#3498db" 102 103 104 darkGrey : String 105 darkGrey = 106 "#2a2929" 107 108 109 red : String 110 red = 111 "#ed4b35" 112 113 114 amber : String 115 amber = 116 "#f5a623" 117 118 119 brown : String 120 brown = 121 "#8b572a" 122 123 124 white : String 125 white = 126 "#ffffff" 127 128 129 fadedGreen : String 130 fadedGreen = 131 "#419867" 132 133 134 orange : String 135 orange = 136 "#e67e22" 137 138 139 pipelineRunningKeyframes : String 140 pipelineRunningKeyframes = 141 "pipeline-running" 142 143 144 flags : Application.Flags 145 flags = 146 { turbulenceImgSrc = "" 147 , notFoundImgSrc = "" 148 , csrfToken = csrfToken 149 , authToken = "" 150 , pipelineRunningKeyframes = pipelineRunningKeyframes 151 } 152 153 154 all : Test 155 all = 156 describe "Dashboard" 157 [ test "requests screen size on page load" <| 158 \_ -> 159 Application.init 160 { turbulenceImgSrc = "" 161 , notFoundImgSrc = "notfound.svg" 162 , csrfToken = "csrf_token" 163 , authToken = "" 164 , pipelineRunningKeyframes = "pipeline-running" 165 } 166 { protocol = Url.Http 167 , host = "" 168 , port_ = Nothing 169 , path = "/" 170 , query = Nothing 171 , fragment = Nothing 172 } 173 |> Tuple.second 174 |> Common.contains Effects.GetScreenSize 175 , test "requests cluster info on page load" <| 176 \_ -> 177 Application.init 178 { turbulenceImgSrc = "" 179 , notFoundImgSrc = "notfound.svg" 180 , csrfToken = "csrf_token" 181 , authToken = "" 182 , pipelineRunningKeyframes = "pipeline-running" 183 } 184 { protocol = Url.Http 185 , host = "" 186 , port_ = Nothing 187 , path = "/" 188 , query = Nothing 189 , fragment = Nothing 190 } 191 |> Tuple.second 192 |> Common.contains Effects.FetchClusterInfo 193 , test "requests all resources on page load" <| 194 \_ -> 195 Application.init 196 { turbulenceImgSrc = "" 197 , notFoundImgSrc = "notfound.svg" 198 , csrfToken = "csrf_token" 199 , authToken = "" 200 , pipelineRunningKeyframes = "" 201 } 202 { protocol = Url.Http 203 , host = "" 204 , port_ = Nothing 205 , path = "/" 206 , query = Nothing 207 , fragment = Nothing 208 } 209 |> Tuple.second 210 |> Common.contains Effects.FetchAllResources 211 , test "requests all jobs on page load" <| 212 \_ -> 213 Application.init 214 { turbulenceImgSrc = "" 215 , notFoundImgSrc = "notfound.svg" 216 , csrfToken = "csrf_token" 217 , authToken = "" 218 , pipelineRunningKeyframes = "" 219 } 220 { protocol = Url.Http 221 , host = "" 222 , port_ = Nothing 223 , path = "/" 224 , query = Nothing 225 , fragment = Nothing 226 } 227 |> Tuple.second 228 |> Common.contains Effects.FetchAllJobs 229 , test "requests all pipelines on page load" <| 230 \_ -> 231 Application.init 232 { turbulenceImgSrc = "" 233 , notFoundImgSrc = "notfound.svg" 234 , csrfToken = "csrf_token" 235 , authToken = "" 236 , pipelineRunningKeyframes = "" 237 } 238 { protocol = Url.Http 239 , host = "" 240 , port_ = Nothing 241 , path = "/" 242 , query = Nothing 243 , fragment = Nothing 244 } 245 |> Tuple.second 246 |> Common.contains Effects.FetchAllPipelines 247 , test "redirects to login if any data call gives a 401" <| 248 \_ -> 249 Common.init "/" 250 |> Application.handleCallback 251 (Callback.AllTeamsFetched <| 252 Data.httpUnauthorized 253 ) 254 |> Tuple.second 255 |> Expect.equal [ Effects.RedirectToLogin ] 256 , test "retries the request after 1 second if ListAllJobs call gives a 503" <| 257 \_ -> 258 Common.init "/" 259 |> Application.handleCallback 260 (Callback.AllJobsFetched <| 261 Err <| 262 Http.BadStatus 263 { url = "http://example.com" 264 , status = 265 { code = 503 266 , message = "service unavailable" 267 } 268 , headers = Dict.empty 269 , body = "" 270 } 271 ) 272 |> Tuple.first 273 |> Application.handleDelivery 274 (ClockTicked OneSecond <| 275 Time.millisToPosix 1000 276 ) 277 |> Tuple.second 278 |> Expect.equal [ Effects.FetchAllJobs ] 279 , test "only retries the request once per 503 response" <| 280 \_ -> 281 Common.init "/" 282 |> Application.handleCallback 283 (Callback.AllJobsFetched <| 284 Err <| 285 Http.BadStatus 286 { url = "http://example.com" 287 , status = 288 { code = 503 289 , message = "service unavailable" 290 } 291 , headers = Dict.empty 292 , body = "" 293 } 294 ) 295 |> Tuple.first 296 |> Application.handleDelivery 297 (ClockTicked OneSecond <| 298 Time.millisToPosix 1000 299 ) 300 |> Tuple.first 301 |> Application.handleDelivery 302 (ClockTicked OneSecond <| 303 Time.millisToPosix 1000 304 ) 305 |> Tuple.second 306 |> Expect.equal [] 307 , test "does not show turbulence screen on 503" <| 308 \_ -> 309 Common.init "/" 310 |> Application.handleCallback 311 (Callback.AllJobsFetched <| 312 Err <| 313 Http.BadStatus 314 { url = "http://example.com" 315 , status = 316 { code = 503 317 , message = "service unavailable" 318 } 319 , headers = Dict.empty 320 , body = "" 321 } 322 ) 323 |> Tuple.first 324 |> Common.queryView 325 |> Query.hasNot [ text "experiencing turbulence" ] 326 , test "shows turbulence view if the all teams call gives a bad status error" <| 327 \_ -> 328 Common.init "/" 329 |> Application.handleCallback 330 (Callback.AllTeamsFetched <| Data.httpInternalServerError) 331 |> Tuple.first 332 |> Common.queryView 333 |> Query.has [ text "experiencing turbulence" ] 334 , test "recovers from turbulence view if all teams call succeeds" <| 335 \_ -> 336 Common.init "/" 337 |> Application.handleCallback 338 (Callback.AllTeamsFetched <| Data.httpInternalServerError) 339 |> Tuple.first 340 |> Application.handleCallback 341 (Callback.AllTeamsFetched <| Ok []) 342 |> Tuple.first 343 |> Common.queryView 344 |> Query.hasNot [ text "experiencing turbulence" ] 345 , test "shows turbulence view if the all resources call gives a bad status error" <| 346 \_ -> 347 Common.init "/" 348 |> Application.handleCallback 349 (Callback.AllResourcesFetched <| Data.httpInternalServerError) 350 |> Tuple.first 351 |> Common.queryView 352 |> Query.has [ text "experiencing turbulence" ] 353 , test "recovers from turbulence view if all resources call succeeds" <| 354 \_ -> 355 Common.init "/" 356 |> Application.handleCallback 357 (Callback.AllResourcesFetched <| Data.httpInternalServerError) 358 |> Tuple.first 359 |> Application.handleCallback 360 (Callback.AllResourcesFetched <| Ok []) 361 |> Tuple.first 362 |> Common.queryView 363 |> Query.hasNot [ text "experiencing turbulence" ] 364 , test "shows turbulence view if the all jobs call gives a bad status error" <| 365 \_ -> 366 Common.init "/" 367 |> Application.handleCallback 368 (Callback.AllJobsFetched <| Data.httpInternalServerError) 369 |> Tuple.first 370 |> Common.queryView 371 |> Query.has [ text "experiencing turbulence" ] 372 , test "recovers from turbulence view if all jobs call succeeds" <| 373 \_ -> 374 Common.init "/" 375 |> Application.handleCallback 376 (Callback.AllJobsFetched <| Data.httpInternalServerError) 377 |> Tuple.first 378 |> Application.handleCallback 379 (Callback.AllJobsFetched <| Ok []) 380 |> Tuple.first 381 |> Common.queryView 382 |> Query.hasNot [ text "experiencing turbulence" ] 383 , test "shows turbulence view if the all pipelines call gives a bad status error" <| 384 \_ -> 385 Common.init "/" 386 |> Application.handleCallback 387 (Callback.AllPipelinesFetched <| Data.httpInternalServerError) 388 |> Tuple.first 389 |> Common.queryView 390 |> Query.has [ text "experiencing turbulence" ] 391 , test "recovers from turbulence view if all pipelines call succeeds" <| 392 \_ -> 393 Common.init "/" 394 |> Application.handleCallback 395 (Callback.AllPipelinesFetched <| Data.httpInternalServerError) 396 |> Tuple.first 397 |> Application.handleCallback 398 (Callback.AllPipelinesFetched <| Ok []) 399 |> Tuple.first 400 |> Common.queryView 401 |> Query.hasNot [ text "experiencing turbulence" ] 402 , test "does not recover from turbulence view if some endpoints are still errored" <| 403 \_ -> 404 Common.init "/" 405 |> Application.handleCallback 406 (Callback.AllJobsFetched <| Data.httpInternalServerError) 407 |> Tuple.first 408 |> Application.handleCallback 409 (Callback.AllPipelinesFetched <| Data.httpInternalServerError) 410 |> Tuple.first 411 |> Application.handleCallback 412 (Callback.AllPipelinesFetched <| Ok []) 413 |> Tuple.first 414 |> Common.queryView 415 |> Query.has [ text "experiencing turbulence" ] 416 , test "title says 'Dashboard - Concourse'" <| 417 \_ -> 418 Common.init "/" 419 |> Application.view 420 |> .title 421 |> Expect.equal "Dashboard - Concourse" 422 , test "renders cluster name at top left" <| 423 \_ -> 424 Common.init "/" 425 |> givenClusterInfo "0.0.0-dev" "foobar" 426 |> Tuple.first 427 |> Common.queryView 428 |> Query.find [ id "top-bar-app" ] 429 |> Query.children [] 430 |> Query.first 431 |> Query.has 432 [ style "display" "flex" 433 , style "align-items" "center" 434 , containing 435 [ style "font-size" "21px" 436 , style "color" ColorValues.white 437 , style "letter-spacing" "0.1em" 438 , style "margin-left" "10px" 439 , containing [ text "foobar" ] 440 ] 441 ] 442 , test "top bar is 54px tall" <| 443 \_ -> 444 Common.init "/" 445 |> Common.queryView 446 |> Query.find [ id "top-bar-app" ] 447 |> Query.has [ style "height" "54px" ] 448 , describe "loading section" <| 449 [ test "has a loading section when awaiting API data" <| 450 \_ -> 451 Common.init "/" 452 |> Common.queryView 453 |> Query.has [ class "loading" ] 454 , test "centers spinner" <| 455 \_ -> 456 Common.init "/" 457 |> Common.queryView 458 |> Query.has 459 [ style "display" "flex" 460 , style "justify-content" "center" 461 , style "align-items" "center" 462 , style "width" "100%" 463 , style "height" "100%" 464 ] 465 , test "contains a spinner" <| 466 \_ -> 467 Common.init "/" 468 |> Common.queryView 469 |> Query.find [ class "loading" ] 470 |> Query.has 471 [ style "animation" "container-rotate 1568ms linear infinite" 472 , style "height" "36px" 473 , style "width" "36px" 474 ] 475 ] 476 , test "high density view has no vertical scroll" <| 477 \_ -> 478 whenOnDashboard { highDensity = True } 479 |> givenDataAndUser 480 (apiData [ ( "team", [] ) ]) 481 (userWithRoles []) 482 |> Tuple.first 483 |> Application.handleCallback 484 (Callback.AllPipelinesFetched <| 485 Ok 486 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 487 ) 488 |> Tuple.first 489 |> Common.queryView 490 |> Query.find [ id "page-below-top-bar" ] 491 |> Query.has 492 [ style "height" "100%" 493 , style "box-sizing" "border-box" 494 ] 495 , test "high density body aligns contents vertically" <| 496 \_ -> 497 whenOnDashboard { highDensity = True } 498 |> givenDataAndUser 499 (apiData [ ( "team", [] ) ]) 500 (userWithRoles []) 501 |> Tuple.first 502 |> Application.handleCallback 503 (Callback.AllPipelinesFetched <| 504 Ok 505 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 506 ) 507 |> Tuple.first 508 |> Common.queryView 509 |> Query.find [ id "page-below-top-bar" ] 510 |> Query.has 511 [ style "display" "flex" 512 , style "flex-direction" "column" 513 ] 514 , test "high density pipelines view fills vertical space" <| 515 \_ -> 516 whenOnDashboard { highDensity = True } 517 |> givenDataAndUser 518 (apiData [ ( "team", [] ) ]) 519 (userWithRoles []) 520 |> Tuple.first 521 |> Application.handleCallback 522 (Callback.AllPipelinesFetched <| 523 Ok 524 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 525 ) 526 |> Tuple.first 527 |> Common.queryView 528 |> Query.find [ id "page-below-top-bar" ] 529 |> Query.find [ class "dashboard" ] 530 |> Query.has [ style "flex-grow" "1" ] 531 , test "high density pipelines view has padding" <| 532 \_ -> 533 whenOnDashboard { highDensity = True } 534 |> givenDataAndUser 535 (apiData [ ( "team", [] ) ]) 536 (userWithRoles []) 537 |> Tuple.first 538 |> Application.handleCallback 539 (Callback.AllPipelinesFetched <| 540 Ok 541 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 542 ) 543 |> Tuple.first 544 |> Common.queryView 545 |> Query.find [ id "page-below-top-bar" ] 546 |> Query.find [ class "dashboard" ] 547 |> Query.has [ style "padding" "60px" ] 548 , test "high density pipelines view wraps columns" <| 549 \_ -> 550 whenOnDashboard { highDensity = True } 551 |> givenDataAndUser 552 (apiData [ ( "team", [] ) ]) 553 (userWithRoles []) 554 |> Tuple.first 555 |> Application.handleCallback 556 (Callback.AllPipelinesFetched <| 557 Ok 558 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 559 ) 560 |> Tuple.first 561 |> Common.queryView 562 |> Query.find [ id "page-below-top-bar" ] 563 |> Query.find [ class "dashboard" ] 564 |> Query.has 565 [ style "display" "flex" 566 , style "flex-flow" "column wrap" 567 ] 568 , test "normal density pipelines view has default layout" <| 569 \_ -> 570 whenOnDashboard { highDensity = False } 571 |> givenDataAndUser 572 (apiData [ ( "team", [] ) ]) 573 (userWithRoles []) 574 |> Tuple.first 575 |> Application.handleCallback 576 (Callback.AllPipelinesFetched <| 577 Ok 578 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 579 ) 580 |> Tuple.first 581 |> Common.queryView 582 |> Query.find [ id "page-below-top-bar" ] 583 |> Query.find [ class "dashboard" ] 584 |> Query.has 585 [ style "display" "initial" 586 , style "padding" "0" 587 ] 588 , test "high density view left-aligns contents" <| 589 \_ -> 590 whenOnDashboard { highDensity = True } 591 |> givenDataAndUser 592 (apiData [ ( "team", [] ) ]) 593 (userWithRoles []) 594 |> Tuple.first 595 |> Application.handleCallback 596 (Callback.AllPipelinesFetched <| 597 Ok 598 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 599 ) 600 |> Tuple.first 601 |> Common.queryView 602 |> Query.find [ id "page-below-top-bar" ] 603 |> Query.find [ class "dashboard" ] 604 |> Query.has [ style "align-content" "flex-start" ] 605 , test "high density view has no overlapping top bar" <| 606 \_ -> 607 whenOnDashboard { highDensity = True } 608 |> Common.queryView 609 |> Query.find [ id "page-below-top-bar" ] 610 |> Query.has [ style "padding-top" "54px" ] 611 , test "high density view has no overlapping bottom bar" <| 612 \_ -> 613 whenOnDashboard { highDensity = True } 614 |> givenDataAndUser 615 (apiData [ ( "team", [] ) ]) 616 (userWithRoles []) 617 |> Tuple.first 618 |> Application.handleCallback 619 (Callback.AllPipelinesFetched <| 620 Ok 621 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 622 ) 623 |> Tuple.first 624 |> Common.queryView 625 |> Query.find [ id "page-below-top-bar" ] 626 |> Query.has [ style "padding-bottom" "50px" ] 627 , test "no bottom padding when footer dismisses" <| 628 \_ -> 629 whenOnDashboard { highDensity = True } 630 |> givenDataAndUser 631 (apiData [ ( "team", [] ) ]) 632 (userWithRoles []) 633 |> Tuple.first 634 |> Application.handleCallback 635 (Callback.AllPipelinesFetched <| 636 Ok 637 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 638 ) 639 |> Tuple.first 640 |> afterSeconds 6 641 |> Common.queryView 642 |> Query.find [ id "page-below-top-bar" ] 643 |> Query.hasNot [ style "padding-bottom" "50px" ] 644 , test "logging out causes pipeline list to reload" <| 645 let 646 showsLoadingState : Application.Model -> Expectation 647 showsLoadingState = 648 Common.queryView 649 >> Query.findAll [ class "dashboard-team-group" ] 650 >> Query.count (Expect.equal 0) 651 in 652 \_ -> 653 whenOnDashboard { highDensity = False } 654 |> givenDataAndUser 655 (apiData [ ( "team", [] ) ]) 656 (userWithRoles [ ( "team", [ "owner" ] ) ]) 657 |> Tuple.first 658 |> Application.handleCallback 659 (Callback.AllPipelinesFetched <| 660 Ok 661 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 662 ) 663 |> Tuple.first 664 |> Application.update 665 (ApplicationMsgs.Update <| 666 Msgs.Click Msgs.LogoutButton 667 ) 668 |> Tuple.first 669 |> showsLoadingState 670 , test "pipeline cards continue to show when teams refresh" <| 671 \_ -> 672 whenOnDashboard { highDensity = False } 673 |> Application.handleCallback 674 (Callback.AllPipelinesFetched <| 675 Ok 676 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 677 ) 678 |> Tuple.first 679 |> givenDataUnauthenticated [] 680 |> Tuple.first 681 |> Common.queryView 682 |> Query.has [ class "card", containing [ text "pipeline" ] ] 683 , test "high-density pipeline cards continue to show when teams refresh" <| 684 \_ -> 685 whenOnDashboard { highDensity = True } 686 |> Application.handleCallback 687 (Callback.AllPipelinesFetched <| 688 Ok 689 [ Data.pipeline "team" 0 |> Data.withName "a-pipeline" ] 690 ) 691 |> Tuple.first 692 |> givenDataUnauthenticated [] 693 |> Tuple.first 694 |> Common.queryView 695 |> Query.has [ class "card", containing [ text "a-pipeline" ] ] 696 , test "links to specific builds" <| 697 \_ -> 698 whenOnDashboard { highDensity = False } 699 |> Application.handleCallback 700 (Callback.AllJobsFetched <| 701 Ok 702 [ { name = "job" 703 , pipelineName = "pipeline" 704 , teamName = "team" 705 , nextBuild = Nothing 706 , finishedBuild = 707 Just 708 { id = 0 709 , name = "1" 710 , job = Just Data.jobId 711 , status = BuildStatusSucceeded 712 , duration = { startedAt = Nothing, finishedAt = Nothing } 713 , reapTime = Nothing 714 } 715 , transitionBuild = Nothing 716 , paused = False 717 , disableManualTrigger = False 718 , inputs = [] 719 , outputs = [] 720 , groups = [] 721 } 722 ] 723 ) 724 |> Tuple.first 725 |> givenDataUnauthenticated [] 726 |> Tuple.first 727 |> Application.handleCallback 728 (Callback.AllPipelinesFetched <| 729 Ok 730 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 731 ) 732 |> Tuple.first 733 |> Common.queryView 734 |> Query.find 735 [ class "dashboard-team-group" 736 , attribute <| Attr.attribute "data-team-name" "team" 737 ] 738 |> Query.find 739 [ attribute <| Attr.attribute "data-tooltip" "job" ] 740 |> Query.find 741 [ tag "a" ] 742 |> Query.has 743 [ attribute <| 744 Attr.href "/teams/team/pipelines/pipeline/jobs/job/builds/1" 745 ] 746 , test "HD view redirects to no pipelines view when there are no pipelines" <| 747 \_ -> 748 whenOnDashboard { highDensity = True } 749 |> Application.handleCallback 750 (Callback.AllTeamsFetched <| 751 Ok <| 752 apiData [ ( "team", [] ) ] 753 ) 754 |> Tuple.first 755 |> Application.handleCallback 756 (Callback.AllPipelinesFetched <| 757 Ok [] 758 ) 759 |> Expect.all 760 [ Tuple.second 761 >> Common.contains (Effects.ModifyUrl "/") 762 , Tuple.first 763 >> Common.queryView 764 >> Query.has [ text "welcome to concourse!" ] 765 ] 766 , test "HD view does not redirect when there are pipelines" <| 767 \_ -> 768 whenOnDashboard { highDensity = True } 769 |> Application.handleCallback 770 (Callback.AllTeamsFetched <| 771 Ok <| 772 apiData [ ( "team", [] ) ] 773 ) 774 |> Tuple.first 775 |> Application.handleCallback 776 (Callback.AllPipelinesFetched <| 777 Ok 778 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 779 ) 780 |> Expect.all 781 [ Tuple.second 782 >> Expect.notEqual [ Effects.ModifyUrl "/" ] 783 , Tuple.first 784 >> Common.queryView 785 >> Query.hasNot [ text "welcome to concourse!" ] 786 ] 787 , test "no search bar when there are no pipelines" <| 788 \_ -> 789 whenOnDashboard { highDensity = False } 790 |> Application.handleCallback 791 (Callback.AllTeamsFetched <| 792 Ok <| 793 apiData [ ( "team", [] ) ] 794 ) 795 |> Tuple.first 796 |> Common.queryView 797 |> Query.hasNot [ tag "input" ] 798 , test "typing '?' in search bar does not toggle help" <| 799 \_ -> 800 whenOnDashboard { highDensity = False } 801 |> Application.handleCallback 802 (Callback.AllTeamsFetched <| 803 Ok <| 804 apiData [ ( "team", [] ) ] 805 ) 806 |> Tuple.first 807 |> Application.handleCallback 808 (Callback.AllPipelinesFetched <| 809 Ok 810 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 811 ) 812 |> Tuple.first 813 |> Application.update (ApplicationMsgs.Update Msgs.FocusMsg) 814 |> Tuple.first 815 |> Application.handleDelivery 816 (KeyDown 817 { ctrlKey = False 818 , shiftKey = True 819 , metaKey = False 820 , code = Keyboard.Slash 821 } 822 ) 823 |> Tuple.first 824 |> Common.queryView 825 |> Query.hasNot [ id "keyboard-help" ] 826 , test "bottom bar appears when there are no pipelines" <| 827 \_ -> 828 whenOnDashboard { highDensity = False } 829 |> Application.handleCallback 830 (Callback.AllTeamsFetched <| 831 Ok <| 832 apiData [ ( "team", [] ) ] 833 ) 834 |> Tuple.first 835 |> Common.queryView 836 |> Query.has [ id "dashboard-info" ] 837 , test "bottom bar has no legend when there are no pipelines" <| 838 \_ -> 839 whenOnDashboard { highDensity = False } 840 |> Application.handleCallback 841 (Callback.AllTeamsFetched <| 842 Ok <| 843 apiData [ ( "team", [] ) ] 844 ) 845 |> Tuple.first 846 |> Common.queryView 847 |> Query.hasNot [ id "legend" ] 848 , test "concourse info is right-justified when there are no pipelines" <| 849 \_ -> 850 whenOnDashboard { highDensity = False } 851 |> Application.handleCallback 852 (Callback.AllTeamsFetched <| 853 Ok <| 854 apiData [ ( "team", [] ) ] 855 ) 856 |> Tuple.first 857 |> Common.queryView 858 |> Query.find [ id "dashboard-info" ] 859 |> Query.has [ style "justify-content" "flex-end" ] 860 , test "pressing '?' does nothing when there are no pipelines" <| 861 \_ -> 862 whenOnDashboard { highDensity = False } 863 |> Application.handleCallback 864 (Callback.AllTeamsFetched <| 865 Ok <| 866 apiData [ ( "team", [] ) ] 867 ) 868 |> Tuple.first 869 |> Application.handleDelivery 870 (KeyDown 871 { ctrlKey = False 872 , shiftKey = True 873 , metaKey = False 874 , code = Keyboard.Slash 875 } 876 ) 877 |> Tuple.first 878 |> Common.queryView 879 |> Query.has [ id "dashboard-info" ] 880 , test "on HD view, team names have increased letter spacing" <| 881 \_ -> 882 whenOnDashboard { highDensity = True } 883 |> givenDataAndUser 884 (apiData [ ( "team", [] ) ]) 885 (userWithRoles []) 886 |> Tuple.first 887 |> Application.handleCallback 888 (Callback.AllPipelinesFetched <| 889 Ok 890 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 891 ) 892 |> Tuple.first 893 |> Common.queryView 894 |> Query.find [ class "dashboard-team-name-wrapper" ] 895 |> Query.has [ style "letter-spacing" ".2em" ] 896 , describe "team pills" 897 [ test 898 ("shows team name with no pill when unauthenticated " 899 ++ "and team has an exposed pipeline" 900 ) 901 <| 902 \_ -> 903 whenOnDashboard { highDensity = False } 904 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 905 |> Tuple.first 906 |> Application.handleCallback 907 (Callback.AllPipelinesFetched <| 908 Ok 909 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 910 ) 911 |> Tuple.first 912 |> Common.queryView 913 |> teamHeaderHasNoPill "team" 914 , test "shows OWNER pill on team header for team on which user has owner role" <| 915 \_ -> 916 whenOnDashboard { highDensity = False } 917 |> givenDataAndUser 918 (apiData [ ( "team", [] ) ]) 919 (userWithRoles [ ( "team", [ "owner" ] ) ]) 920 |> Tuple.first 921 |> Application.handleCallback 922 (Callback.AllPipelinesFetched <| 923 Ok 924 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 925 ) 926 |> Tuple.first 927 |> Common.queryView 928 |> teamHeaderHasPill "team" "OWNER" 929 , test "shows MEMBER pill on team header for team on which user has member role" <| 930 \_ -> 931 whenOnDashboard { highDensity = False } 932 |> givenDataAndUser 933 (apiData [ ( "team", [] ) ]) 934 (userWithRoles [ ( "team", [ "member" ] ) ]) 935 |> Tuple.first 936 |> Application.handleCallback 937 (Callback.AllPipelinesFetched <| 938 Ok 939 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 940 ) 941 |> Tuple.first 942 |> Common.queryView 943 |> teamHeaderHasPill "team" "MEMBER" 944 , test "shows PIPELINE_OPERATOR pill on team header for team on which user has member role" <| 945 \_ -> 946 whenOnDashboard { highDensity = False } 947 |> givenDataAndUser 948 (apiData [ ( "team", [] ) ]) 949 (userWithRoles [ ( "team", [ "pipeline-operator" ] ) ]) 950 |> Tuple.first 951 |> Application.handleCallback 952 (Callback.AllPipelinesFetched <| 953 Ok 954 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 955 ) 956 |> Tuple.first 957 |> Common.queryView 958 |> teamHeaderHasPill "team" "PIPELINE_OPERATOR" 959 , test "shows VIEWER pill on team header for team on which user has viewer role" <| 960 \_ -> 961 whenOnDashboard { highDensity = False } 962 |> givenDataAndUser 963 (apiData [ ( "team", [] ) ]) 964 (userWithRoles [ ( "team", [ "viewer" ] ) ]) 965 |> Tuple.first 966 |> Application.handleCallback 967 (Callback.AllPipelinesFetched <| 968 Ok 969 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 970 ) 971 |> Tuple.first 972 |> Common.queryView 973 |> teamHeaderHasPill "team" "VIEWER" 974 , test "shows no pill on team header for team on which user has no role" <| 975 \_ -> 976 whenOnDashboard { highDensity = False } 977 |> givenDataAndUser 978 (apiData [ ( "team", [] ) ]) 979 (userWithRoles []) 980 |> Tuple.first 981 |> Application.handleCallback 982 (Callback.AllPipelinesFetched <| 983 Ok 984 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 985 ) 986 |> Tuple.first 987 |> Common.queryView 988 |> teamHeaderHasNoPill "team" 989 , test 990 ("shows pill for most-privileged role on team header for team " 991 ++ "on which user has multiple roles" 992 ) 993 <| 994 \_ -> 995 whenOnDashboard { highDensity = False } 996 |> givenDataAndUser 997 (apiData [ ( "team", [] ) ]) 998 (userWithRoles [ ( "team", [ "viewer", "member" ] ) ]) 999 |> Tuple.first 1000 |> Application.handleCallback 1001 (Callback.AllPipelinesFetched <| 1002 Ok 1003 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1004 ) 1005 |> Tuple.first 1006 |> Common.queryView 1007 |> teamHeaderHasPill "team" "MEMBER" 1008 , test "sorts teams according to user role" <| 1009 \_ -> 1010 whenOnDashboard { highDensity = False } 1011 |> givenDataAndUser 1012 (apiData 1013 [ ( "owner-team", [] ) 1014 , ( "nonmember-team", [] ) 1015 , ( "viewer-team", [] ) 1016 , ( "member-team", [] ) 1017 ] 1018 ) 1019 (userWithRoles 1020 [ ( "owner-team", [ "owner" ] ) 1021 , ( "member-team", [ "member" ] ) 1022 , ( "viewer-team", [ "viewer" ] ) 1023 , ( "nonmember-team", [] ) 1024 ] 1025 ) 1026 |> Tuple.first 1027 |> Application.handleCallback 1028 (Callback.AllPipelinesFetched <| 1029 Ok 1030 [ Data.pipeline "owner-team" 0 |> Data.withName "pipeline" ] 1031 ) 1032 |> Tuple.first 1033 |> Common.queryView 1034 |> Query.findAll teamHeaderSelector 1035 |> Expect.all 1036 [ Query.count (Expect.equal 4) 1037 , Query.index 0 >> Query.has [ text "owner-team" ] 1038 , Query.index 1 >> Query.has [ text "member-team" ] 1039 , Query.index 2 >> Query.has [ text "viewer-team" ] 1040 , Query.index 3 >> Query.has [ text "nonmember-team" ] 1041 ] 1042 , test "team headers lay out contents horizontally, centering vertically" <| 1043 \_ -> 1044 whenOnDashboard { highDensity = False } 1045 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1046 |> Tuple.first 1047 |> Application.handleCallback 1048 (Callback.AllPipelinesFetched <| 1049 Ok 1050 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1051 ) 1052 |> Tuple.first 1053 |> Common.queryView 1054 |> Query.findAll teamHeaderSelector 1055 |> Query.each 1056 (Query.has 1057 [ style "display" "flex" 1058 , style "align-items" "center" 1059 ] 1060 ) 1061 , test "team headers have a bottom margin of 25px" <| 1062 \_ -> 1063 whenOnDashboard { highDensity = False } 1064 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1065 |> Tuple.first 1066 |> Application.handleCallback 1067 (Callback.AllPipelinesFetched <| 1068 Ok 1069 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1070 ) 1071 |> Tuple.first 1072 |> Common.queryView 1073 |> Query.findAll teamHeaderSelector 1074 |> Query.each 1075 (Query.has [ style "margin-bottom" "25px" ]) 1076 , test "on HD view, there is space between the list of pipelines and the role pill" <| 1077 \_ -> 1078 whenOnDashboard { highDensity = True } 1079 |> givenDataAndUser 1080 (apiData [ ( "team", [] ) ]) 1081 (userWithRoles [ ( "team", [ "owner" ] ) ]) 1082 |> Tuple.first 1083 |> Application.handleCallback 1084 (Callback.AllPipelinesFetched <| 1085 Ok 1086 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1087 ) 1088 |> Tuple.first 1089 |> Common.queryView 1090 |> Query.find [ class "dashboard-team-name-wrapper" ] 1091 |> Query.find [ containing [ text "OWNER" ] ] 1092 |> Query.has [ style "margin-bottom" "1em" ] 1093 , test "on non-HD view, the role pill on a group has no margin below" <| 1094 \_ -> 1095 whenOnDashboard { highDensity = False } 1096 |> givenDataAndUser 1097 (apiData [ ( "team", [] ) ]) 1098 (userWithRoles [ ( "team", [ "owner" ] ) ]) 1099 |> Tuple.first 1100 |> Application.handleCallback 1101 (Callback.AllPipelinesFetched <| 1102 Ok 1103 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1104 ) 1105 |> Tuple.first 1106 |> Common.queryView 1107 |> Query.find teamHeaderSelector 1108 |> Query.find [ containing [ text "OWNER" ] ] 1109 |> Query.has [ style "margin-bottom" "" ] 1110 , test "has momentum based scrolling" <| 1111 \_ -> 1112 whenOnDashboard { highDensity = True } 1113 |> givenDataAndUser 1114 (apiData [ ( "team", [] ) ]) 1115 (userWithRoles []) 1116 |> Tuple.first 1117 |> Application.handleCallback 1118 (Callback.AllPipelinesFetched <| 1119 Ok 1120 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1121 ) 1122 |> Tuple.first 1123 |> Common.queryView 1124 |> Query.find [ id "page-below-top-bar" ] 1125 |> Query.find [ class "dashboard" ] 1126 |> Query.has [ style "-webkit-overflow-scrolling" "touch" ] 1127 ] 1128 , describe "bottom bar" 1129 [ test "appears by default" <| 1130 \_ -> 1131 whenOnDashboard { highDensity = False } 1132 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1133 |> Tuple.first 1134 |> Application.handleCallback 1135 (Callback.AllPipelinesFetched <| 1136 Ok 1137 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1138 ) 1139 |> Tuple.first 1140 |> Common.queryView 1141 |> Query.has [ id "dashboard-info" ] 1142 , test "is 50px tall, almost black, fixed to the bottom of the viewport and covers entire width" <| 1143 \_ -> 1144 whenOnDashboard { highDensity = False } 1145 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1146 |> Tuple.first 1147 |> Application.handleCallback 1148 (Callback.AllPipelinesFetched <| 1149 Ok 1150 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1151 ) 1152 |> Tuple.first 1153 |> Common.queryView 1154 |> Query.find [ id "dashboard-info" ] 1155 |> Query.has 1156 [ style "line-height" "35px" 1157 , style "padding" "7.5px 30px" 1158 , style "position" "fixed" 1159 , style "bottom" "0" 1160 , style "background-color" ColorValues.grey100 1161 , style "width" "100%" 1162 , style "box-sizing" "border-box" 1163 ] 1164 , test "lays out contents horizontally, maximizing space between children" <| 1165 \_ -> 1166 whenOnDashboard { highDensity = False } 1167 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1168 |> Tuple.first 1169 |> Application.handleCallback 1170 (Callback.AllPipelinesFetched <| 1171 Ok 1172 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1173 ) 1174 |> Tuple.first 1175 |> Common.queryView 1176 |> Query.find [ id "dashboard-info" ] 1177 |> Query.has 1178 [ style "display" "flex" 1179 , style "justify-content" "space-between" 1180 ] 1181 , test "has a z-index of 2" <| 1182 \_ -> 1183 whenOnDashboard { highDensity = False } 1184 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1185 |> Tuple.first 1186 |> Application.handleCallback 1187 (Callback.AllPipelinesFetched <| 1188 Ok 1189 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1190 ) 1191 |> Tuple.first 1192 |> Common.queryView 1193 |> Query.find [ id "dashboard-info" ] 1194 |> Query.has [ style "z-index" "2" ] 1195 , test "two children are legend and concourse-info" <| 1196 \_ -> 1197 whenOnDashboard { highDensity = False } 1198 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1199 |> Tuple.first 1200 |> Application.handleCallback 1201 (Callback.AllPipelinesFetched <| 1202 Ok 1203 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1204 ) 1205 |> Tuple.first 1206 |> Common.queryView 1207 |> Query.find [ id "dashboard-info" ] 1208 |> Query.children [] 1209 |> Expect.all 1210 [ Query.count (Expect.equal 2) 1211 , Query.index 0 >> Query.has [ id "legend" ] 1212 , Query.index 1 >> Query.has [ id "concourse-info" ] 1213 ] 1214 , test "lays out children on two lines when view width is below 1230px" <| 1215 \_ -> 1216 Common.init "/" 1217 |> givenDataUnauthenticated 1218 (apiData [ ( "team", [] ) ]) 1219 |> Tuple.first 1220 |> Application.handleCallback 1221 (Callback.AllPipelinesFetched <| 1222 Ok 1223 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1224 ) 1225 |> Tuple.first 1226 |> Application.update 1227 (ApplicationMsgs.DeliveryReceived <| 1228 WindowResized 1229 300 1229 ) 1230 |> Tuple.first 1231 |> Common.queryView 1232 |> Query.find [ id "dashboard-info" ] 1233 |> Query.has [ style "flex-direction" "column" ] 1234 , describe "legend" 1235 [ test "lays out contents horizontally" <| 1236 \_ -> 1237 whenOnDashboard { highDensity = False } 1238 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1239 |> Tuple.first 1240 |> Application.handleCallback 1241 (Callback.AllPipelinesFetched <| 1242 Ok 1243 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1244 ) 1245 |> Tuple.first 1246 |> Common.queryView 1247 |> Query.find [ id "legend" ] 1248 |> Query.has [ style "display" "flex" ] 1249 , test "shows pipeline statuses" <| 1250 \_ -> 1251 whenOnDashboard { highDensity = False } 1252 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1253 |> Tuple.first 1254 |> Application.handleCallback 1255 (Callback.AllPipelinesFetched <| 1256 Ok 1257 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1258 ) 1259 |> Tuple.first 1260 |> Common.queryView 1261 |> Query.find [ id "legend" ] 1262 |> Query.children [] 1263 |> Expect.all 1264 [ Query.count (Expect.equal 9) 1265 , Query.index 0 1266 >> Query.children [] 1267 >> Expect.all 1268 [ Query.count (Expect.equal 3) 1269 , Query.index 0 1270 >> Query.has 1271 (iconSelector 1272 { size = "20px" 1273 , image = PipelineStatusPending True |> Assets.PipelineStatusIcon 1274 } 1275 ) 1276 , Query.index 1 1277 >> Query.has [ style "width" "10px" ] 1278 , Query.index 2 >> Query.has [ text "pending" ] 1279 ] 1280 , Query.index 1 1281 >> Query.children [] 1282 >> Expect.all 1283 [ Query.count (Expect.equal 3) 1284 , Query.index 0 1285 >> Query.has 1286 (iconSelector 1287 { size = "20px" 1288 , image = PipelineStatusPaused |> Assets.PipelineStatusIcon 1289 } 1290 ) 1291 , Query.index 1 1292 >> Query.has [ style "width" "10px" ] 1293 , Query.index 2 >> Query.has [ text "paused" ] 1294 ] 1295 ] 1296 , test "the legend separator is grey" <| 1297 \_ -> 1298 whenOnDashboard { highDensity = False } 1299 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1300 |> Tuple.first 1301 |> Application.handleCallback 1302 (Callback.AllPipelinesFetched <| 1303 Ok 1304 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1305 ) 1306 |> Tuple.first 1307 |> Common.queryView 1308 |> Query.find [ id "legend" ] 1309 |> Query.children [] 1310 |> Query.index -2 1311 |> Query.has [ style "color" ColorValues.grey40 ] 1312 , test "the legend separator centers contents vertically" <| 1313 \_ -> 1314 whenOnDashboard { highDensity = False } 1315 |> givenDataUnauthenticated 1316 (apiData [ ( "team", [] ) ]) 1317 |> Tuple.first 1318 |> Application.handleCallback 1319 (Callback.AllPipelinesFetched <| 1320 Ok 1321 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1322 ) 1323 |> Tuple.first 1324 |> Common.queryView 1325 |> Query.find [ id "legend" ] 1326 |> Query.children [] 1327 |> Query.index -2 1328 |> Query.has [ style "display" "flex", style "align-items" "center" ] 1329 , test "the legend separator is gone when the window width is below 812px" <| 1330 \_ -> 1331 Common.init "/" 1332 |> givenDataUnauthenticated 1333 (apiData [ ( "team", [] ) ]) 1334 |> Tuple.first 1335 |> Application.handleCallback 1336 (Callback.AllPipelinesFetched <| 1337 Ok 1338 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1339 ) 1340 |> Tuple.first 1341 |> Application.update 1342 (ApplicationMsgs.DeliveryReceived <| 1343 WindowResized 800 300 1344 ) 1345 |> Tuple.first 1346 |> Common.queryView 1347 |> Query.find [ id "legend" ] 1348 |> Expect.all 1349 [ Query.hasNot [ text "|" ] 1350 , Query.children [] >> Query.count (Expect.equal 8) 1351 ] 1352 , test "legend items wrap when window width is below 812px" <| 1353 \_ -> 1354 Common.init "/" 1355 |> givenDataUnauthenticated 1356 (apiData [ ( "team", [] ) ]) 1357 |> Tuple.first 1358 |> Application.handleCallback 1359 (Callback.AllPipelinesFetched <| 1360 Ok 1361 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1362 ) 1363 |> Tuple.first 1364 |> Application.update 1365 (ApplicationMsgs.DeliveryReceived <| 1366 WindowResized 800 300 1367 ) 1368 |> Tuple.first 1369 |> Common.queryView 1370 |> Query.find [ id "legend" ] 1371 |> Query.has [ style "flex-wrap" "wrap" ] 1372 , test "legend items lay out contents horizontally, centered vertically in grey caps" <| 1373 \_ -> 1374 whenOnDashboard { highDensity = False } 1375 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1376 |> Tuple.first 1377 |> Application.handleCallback 1378 (Callback.AllPipelinesFetched <| 1379 Ok 1380 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1381 ) 1382 |> Tuple.first 1383 |> Common.queryView 1384 |> Query.find [ id "legend" ] 1385 |> Query.children [] 1386 |> Query.index 0 1387 |> Query.has 1388 [ style "text-transform" "uppercase" 1389 , style "display" "flex" 1390 , style "align-items" "center" 1391 , style "color" ColorValues.grey40 1392 ] 1393 , test "legend items have 20px space between them" <| 1394 \_ -> 1395 whenOnDashboard { highDensity = False } 1396 |> givenDataUnauthenticated 1397 (apiData [ ( "team", [] ) ]) 1398 |> Tuple.first 1399 |> Application.handleCallback 1400 (Callback.AllPipelinesFetched <| 1401 Ok 1402 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1403 ) 1404 |> Tuple.first 1405 |> Common.queryView 1406 |> Query.find [ id "legend" ] 1407 |> Query.children [] 1408 |> Query.each 1409 (Query.has [ style "margin-right" "20px" ]) 1410 , test "third legend item shows running indicator" <| 1411 \_ -> 1412 whenOnDashboard { highDensity = False } 1413 |> givenDataUnauthenticated 1414 (apiData [ ( "team", [] ) ]) 1415 |> Tuple.first 1416 |> Application.handleCallback 1417 (Callback.AllPipelinesFetched <| 1418 Ok 1419 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1420 ) 1421 |> Tuple.first 1422 |> Common.queryView 1423 |> Query.find [ id "legend" ] 1424 |> Query.children [] 1425 |> Query.index 2 1426 |> Expect.all 1427 [ Query.has 1428 [ style "text-transform" "uppercase" 1429 , style "display" "flex" 1430 ] 1431 , Query.children [] 1432 >> Expect.all 1433 [ Query.count (Expect.equal 3) 1434 , Query.index 0 1435 >> Query.has 1436 (iconSelector 1437 { size = "20px" 1438 , image = Assets.RunningLegend 1439 } 1440 ) 1441 , Query.index 1 1442 >> Query.has 1443 [ style "width" "10px" ] 1444 , Query.index 2 >> Query.has [ text "running" ] 1445 ] 1446 ] 1447 ] 1448 , describe "HD toggle" <| 1449 let 1450 findHDToggle = 1451 Query.find [ id "legend" ] 1452 >> Query.children [] 1453 >> Query.index -1 1454 1455 hdToggle = 1456 whenOnDashboard { highDensity = False } 1457 |> givenDataUnauthenticated 1458 (apiData [ ( "team", [] ) ]) 1459 |> Tuple.first 1460 |> Application.handleCallback 1461 (Callback.AllPipelinesFetched <| 1462 Ok 1463 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1464 ) 1465 |> Tuple.first 1466 |> Common.queryView 1467 |> findHDToggle 1468 in 1469 [ describe "on non-hd view" 1470 [ test "lays out contents horizontally" <| 1471 \_ -> 1472 hdToggle 1473 |> Query.has [ style "display" "flex" ] 1474 , test "centers contents vertically" <| 1475 \_ -> 1476 hdToggle 1477 |> Query.has [ style "align-items" "center" ] 1478 , test "has a margin of 10px between the button and the label" <| 1479 \_ -> 1480 hdToggle 1481 |> Query.children [] 1482 |> Query.index 0 1483 |> Query.has 1484 [ style "margin-right" "10px" ] 1485 , test "displays the label using a grey color" <| 1486 \_ -> 1487 hdToggle 1488 |> Query.has [ style "color" ColorValues.grey40 ] 1489 , test "label text is all caps" <| 1490 \_ -> 1491 hdToggle 1492 |> Query.has 1493 [ style "text-transform" "uppercase" ] 1494 , test "displays label on the right" <| 1495 \_ -> 1496 hdToggle 1497 |> Query.has [ style "flex-direction" "row" ] 1498 , test "links to HD view" <| 1499 \_ -> 1500 hdToggle 1501 |> Query.has [ attribute <| Attr.href "/hd" ] 1502 , test "displays the off state" <| 1503 \_ -> 1504 hdToggle 1505 |> Query.children [] 1506 |> Query.index 0 1507 |> Query.has 1508 [ style "background-image" <| 1509 Assets.backgroundImage <| 1510 Just (Assets.ToggleSwitch False) 1511 , style "background-size" "contain" 1512 , style "height" "20px" 1513 , style "width" "35px" 1514 ] 1515 , test "will not shrink on resizing" <| 1516 \_ -> 1517 hdToggle 1518 |> Query.children [] 1519 |> Query.index 0 1520 |> Query.has [ style "flex-shrink" "0" ] 1521 ] 1522 , describe "on HD view" 1523 [ test "displays the on state" <| 1524 \_ -> 1525 whenOnDashboard { highDensity = True } 1526 |> givenDataUnauthenticated 1527 (apiData [ ( "team", [] ) ]) 1528 |> Tuple.first 1529 |> Application.handleCallback 1530 (Callback.AllPipelinesFetched <| 1531 Ok 1532 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1533 ) 1534 |> Tuple.first 1535 |> Common.queryView 1536 |> findHDToggle 1537 |> Query.children [] 1538 |> Query.index 0 1539 |> Query.has 1540 [ style "background-image" <| 1541 Assets.backgroundImage <| 1542 Just (Assets.ToggleSwitch True) 1543 , style "background-size" "contain" 1544 , style "height" "20px" 1545 , style "width" "35px" 1546 ] 1547 , test "links to normal dashboard view" <| 1548 \_ -> 1549 whenOnDashboard { highDensity = True } 1550 |> givenDataUnauthenticated 1551 (apiData [ ( "team", [] ) ]) 1552 |> Tuple.first 1553 |> Application.handleCallback 1554 (Callback.AllPipelinesFetched <| 1555 Ok 1556 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1557 ) 1558 |> Tuple.first 1559 |> Common.queryView 1560 |> findHDToggle 1561 |> Query.has [ attribute <| Attr.href "/" ] 1562 , test "will not shrink on resizing" <| 1563 \_ -> 1564 whenOnDashboard { highDensity = True } 1565 |> givenDataUnauthenticated 1566 (apiData [ ( "team", [] ) ]) 1567 |> Tuple.first 1568 |> Application.handleCallback 1569 (Callback.AllPipelinesFetched <| 1570 Ok 1571 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1572 ) 1573 |> Tuple.first 1574 |> Common.queryView 1575 |> findHDToggle 1576 |> Query.children [] 1577 |> Query.index 0 1578 |> Query.has 1579 [ style "flex-shrink" "0" ] 1580 ] 1581 ] 1582 , describe "when there are favorited pipelines" <| 1583 let 1584 setup params = 1585 whenOnDashboard params 1586 |> givenDataUnauthenticated 1587 (apiData [ ( "team", [] ) ]) 1588 |> Tuple.first 1589 |> Application.handleCallback 1590 (Callback.AllPipelinesFetched <| 1591 Ok 1592 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1593 ) 1594 |> Tuple.first 1595 |> Application.handleDelivery 1596 (Subscription.FavoritedPipelinesReceived <| 1597 Ok <| 1598 Set.singleton 0 1599 ) 1600 in 1601 [ test "displays favorite pipelines header" <| 1602 \_ -> 1603 setup { highDensity = False } 1604 |> Tuple.first 1605 |> Common.queryView 1606 |> Query.has [ text "favorite pipelines" ] 1607 , test "does not display header when on the HD view" <| 1608 \_ -> 1609 setup { highDensity = True } 1610 |> Tuple.first 1611 |> Common.queryView 1612 |> Query.hasNot [ text "favorite pipelines" ] 1613 ] 1614 , describe "when there are no favorited pipelines" 1615 [ test "does not display header" <| 1616 \_ -> 1617 whenOnDashboard { highDensity = False } 1618 |> givenDataUnauthenticated 1619 (apiData [ ( "team", [] ) ]) 1620 |> Tuple.first 1621 |> Application.handleCallback 1622 (Callback.AllPipelinesFetched <| 1623 Ok 1624 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1625 ) 1626 |> Tuple.first 1627 |> Common.queryView 1628 |> Query.hasNot [ text "favorite pipelines" ] 1629 ] 1630 , describe "all pipelines header" 1631 [ test "displayed when there are pipelines" <| 1632 \_ -> 1633 whenOnDashboard { highDensity = False } 1634 |> givenDataUnauthenticated 1635 (apiData [ ( "team", [] ) ]) 1636 |> Tuple.first 1637 |> Application.handleCallback 1638 (Callback.AllPipelinesFetched <| 1639 Ok 1640 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1641 ) 1642 |> Tuple.first 1643 |> Common.queryView 1644 |> Query.has [ text "all pipelines" ] 1645 , test "displayed when there are no pipelines" <| 1646 \_ -> 1647 whenOnDashboard { highDensity = False } 1648 |> givenDataUnauthenticated 1649 (apiData [ ( "team", [] ) ]) 1650 |> Tuple.first 1651 |> Application.handleCallback 1652 (Callback.AllPipelinesFetched <| 1653 Ok [] 1654 ) 1655 |> Tuple.first 1656 |> Common.queryView 1657 |> Query.has [ text "all pipelines" ] 1658 , test "not displayed when there are no teams" <| 1659 \_ -> 1660 whenOnDashboard { highDensity = False } 1661 |> givenDataUnauthenticated 1662 (apiData []) 1663 |> Tuple.first 1664 |> Application.handleCallback 1665 (Callback.AllPipelinesFetched <| 1666 Ok [] 1667 ) 1668 |> Tuple.first 1669 |> Common.queryView 1670 |> Query.hasNot [ text "all pipelines" ] 1671 ] 1672 , describe "info section" <| 1673 let 1674 info = 1675 whenOnDashboard { highDensity = False } 1676 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1677 |> Tuple.first 1678 |> Application.handleCallback 1679 (Callback.AllPipelinesFetched <| 1680 Ok 1681 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1682 ) 1683 |> Tuple.first 1684 |> Common.queryView 1685 |> Query.find [ id "concourse-info" ] 1686 in 1687 [ test "lays out contents horizontally" <| 1688 \_ -> 1689 info 1690 |> Query.has [ style "display" "flex" ] 1691 , test "displays info in a grey color" <| 1692 \_ -> 1693 info 1694 |> Query.has [ style "color" ColorValues.grey40 ] 1695 , test "displays text slightly larger" <| 1696 \_ -> 1697 info 1698 |> Query.has [ style "font-size" "1.25em" ] 1699 , test "each info item is spaced out by 30px" <| 1700 \_ -> 1701 info 1702 |> Query.children [] 1703 |> Query.each 1704 (Query.has [ style "margin-right" "30px" ]) 1705 , test "each info item centers contents vertically" <| 1706 \_ -> 1707 info 1708 |> Query.children [] 1709 |> Query.each 1710 (Query.has 1711 [ style "align-items" "center" 1712 , style "display" "flex" 1713 ] 1714 ) 1715 , test "items in CLI section are 10 px apart" <| 1716 \_ -> 1717 info 1718 |> Query.children [] 1719 |> Query.index -1 1720 |> Query.children [] 1721 |> Query.each 1722 (Query.has [ style "margin-right" "10px" ]) 1723 , describe "cli download icons" <| 1724 let 1725 cliIcons = 1726 info 1727 |> Query.children [] 1728 |> Query.index -1 1729 |> Query.children [ tag "a" ] 1730 in 1731 [ test "icons are grey" <| 1732 \_ -> 1733 cliIcons 1734 |> Query.each 1735 (Query.has [ style "opacity" "0.5" ]) 1736 , test "have 'download' attribute" <| 1737 \_ -> 1738 cliIcons 1739 |> Query.each 1740 (Query.has 1741 [ attribute <| Attr.download "" ] 1742 ) 1743 , test "icons have descriptive ARIA labels" <| 1744 \_ -> 1745 cliIcons 1746 |> Expect.all 1747 [ Query.count (Expect.equal 3) 1748 , Query.index 0 1749 >> Query.has 1750 [ attribute <| 1751 Attr.attribute 1752 "aria-label" 1753 "Download OS X CLI" 1754 ] 1755 , Query.index 1 1756 >> Query.has 1757 [ attribute <| 1758 Attr.attribute 1759 "aria-label" 1760 "Download Windows CLI" 1761 ] 1762 , Query.index 2 1763 >> Query.has 1764 [ attribute <| 1765 Attr.attribute 1766 "aria-label" 1767 "Download Linux CLI" 1768 ] 1769 ] 1770 , defineHoverBehaviour 1771 { name = "os x cli icon" 1772 , setup = 1773 whenOnDashboard { highDensity = False } 1774 |> givenDataUnauthenticated 1775 (apiData [ ( "team", [] ) ]) 1776 |> Tuple.first 1777 |> Application.handleCallback 1778 (Callback.AllPipelinesFetched <| 1779 Ok 1780 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1781 ) 1782 |> Tuple.first 1783 , query = Common.queryView >> Query.find [ id "cli-osx" ] 1784 , unhoveredSelector = 1785 { description = "grey apple icon" 1786 , selector = 1787 [ style "opacity" "0.5" 1788 , style "background-size" "contain" 1789 ] 1790 ++ iconSelector 1791 { image = Assets.CliIcon Cli.OSX 1792 , size = "20px" 1793 } 1794 } 1795 , hoverable = 1796 Msgs.FooterCliIcon Cli.OSX 1797 , hoveredSelector = 1798 { description = "white apple icon" 1799 , selector = 1800 [ style "opacity" "1" 1801 , style "background-size" "contain" 1802 ] 1803 ++ iconSelector 1804 { image = Assets.CliIcon Cli.OSX 1805 , size = "20px" 1806 } 1807 } 1808 } 1809 , defineHoverBehaviour 1810 { name = "windows cli icon" 1811 , setup = 1812 whenOnDashboard { highDensity = False } 1813 |> givenDataUnauthenticated 1814 (apiData [ ( "team", [] ) ]) 1815 |> Tuple.first 1816 |> Application.handleCallback 1817 (Callback.AllPipelinesFetched <| 1818 Ok 1819 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1820 ) 1821 |> Tuple.first 1822 , query = 1823 Common.queryView 1824 >> Query.find [ id "cli-windows" ] 1825 , unhoveredSelector = 1826 { description = "grey windows icon" 1827 , selector = 1828 [ style "opacity" "0.5" 1829 , style "background-size" "contain" 1830 ] 1831 ++ iconSelector 1832 { image = Assets.CliIcon Cli.Windows 1833 , size = "20px" 1834 } 1835 } 1836 , hoverable = 1837 Msgs.FooterCliIcon Cli.Windows 1838 , hoveredSelector = 1839 { description = "white windows icon" 1840 , selector = 1841 [ style "opacity" "1" 1842 , style "background-size" "contain" 1843 ] 1844 ++ iconSelector 1845 { image = Assets.CliIcon Cli.Windows 1846 , size = "20px" 1847 } 1848 } 1849 } 1850 , defineHoverBehaviour 1851 { name = "linux cli icon" 1852 , setup = 1853 whenOnDashboard { highDensity = False } 1854 |> givenDataUnauthenticated 1855 (apiData 1856 [ ( "team", [] ) ] 1857 ) 1858 |> Tuple.first 1859 |> Application.handleCallback 1860 (Callback.AllPipelinesFetched <| 1861 Ok 1862 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1863 ) 1864 |> Tuple.first 1865 , query = 1866 Common.queryView 1867 >> Query.find [ id "cli-linux" ] 1868 , unhoveredSelector = 1869 { description = "grey linux icon" 1870 , selector = 1871 [ style "opacity" "0.5" 1872 , style "background-size" "contain" 1873 ] 1874 ++ iconSelector 1875 { image = Assets.CliIcon Cli.Linux 1876 , size = "20px" 1877 } 1878 } 1879 , hoverable = 1880 Msgs.FooterCliIcon Cli.Linux 1881 , hoveredSelector = 1882 { description = "white linux icon" 1883 , selector = 1884 [ style "opacity" "1" 1885 , style "background-size" "contain" 1886 ] 1887 ++ iconSelector 1888 { image = Assets.CliIcon Cli.Linux 1889 , size = "20px" 1890 } 1891 } 1892 } 1893 ] 1894 , test "shows concourse version" <| 1895 \_ -> 1896 whenOnDashboard { highDensity = False } 1897 |> givenClusterInfo "1.2.3" "cluster-name" 1898 |> Tuple.first 1899 |> Common.queryView 1900 |> Query.find [ id "concourse-info" ] 1901 |> Query.has [ text "v1.2.3" ] 1902 ] 1903 , test "hides after 6 seconds" <| 1904 \_ -> 1905 Common.init "/" 1906 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1907 |> Tuple.first 1908 |> Application.handleCallback 1909 (Callback.AllPipelinesFetched <| 1910 Ok 1911 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1912 ) 1913 |> Tuple.first 1914 |> afterSeconds 6 1915 |> Common.queryView 1916 |> Query.hasNot [ id "dashboard-info" ] 1917 , test "reappears on mouse action" <| 1918 \_ -> 1919 Common.init "/" 1920 |> givenDataUnauthenticated (apiData [ ( "team", [] ) ]) 1921 |> Tuple.first 1922 |> Application.handleCallback 1923 (Callback.AllPipelinesFetched <| 1924 Ok 1925 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1926 ) 1927 |> Tuple.first 1928 |> afterSeconds 6 1929 |> Application.update 1930 (ApplicationMsgs.DeliveryReceived <| Moused { x = 0, y = 0 }) 1931 |> Tuple.first 1932 |> Common.queryView 1933 |> Query.has [ id "dashboard-info" ] 1934 , test "is replaced by keyboard help when pressing '?'" <| 1935 \_ -> 1936 Common.init "/" 1937 |> givenDataUnauthenticated 1938 (apiData [ ( "team", [] ) ]) 1939 |> Tuple.first 1940 |> Application.handleCallback 1941 (Callback.AllPipelinesFetched <| 1942 Ok 1943 [ Data.pipeline "team" 0 |> Data.withName "pipeline" ] 1944 ) 1945 |> Tuple.first 1946 |> Application.update 1947 (ApplicationMsgs.DeliveryReceived <| 1948 KeyDown 1949 { ctrlKey = False 1950 , shiftKey = True 1951 , metaKey = False 1952 , code = Keyboard.Slash 1953 } 1954 ) 1955 |> Tuple.first 1956 |> Common.queryView 1957 |> Expect.all 1958 [ Query.hasNot [ id "dashboard-info" ] 1959 , Query.has [ id "keyboard-help" ] 1960 ] 1961 ] 1962 , test "subscribes to one and five second timers" <| 1963 \_ -> 1964 whenOnDashboard { highDensity = False } 1965 |> Application.subscriptions 1966 |> Expect.all 1967 [ Common.contains (Subscription.OnClockTick OneSecond) 1968 , Common.contains (Subscription.OnClockTick FiveSeconds) 1969 ] 1970 , test "subscribes to keyups" <| 1971 \_ -> 1972 whenOnDashboard { highDensity = False } 1973 |> Application.subscriptions 1974 |> Common.contains Subscription.OnKeyUp 1975 , test "auto refreshes jobs on five-second tick after previous request finishes" <| 1976 \_ -> 1977 Common.init "/" 1978 |> Application.handleCallback 1979 (Callback.AllJobsFetched <| Ok []) 1980 |> Tuple.first 1981 |> Application.handleDelivery 1982 (ClockTicked FiveSeconds <| 1983 Time.millisToPosix 0 1984 ) 1985 |> Tuple.second 1986 |> Common.contains Effects.FetchAllJobs 1987 , test "stops polling jobs if the endpoint is disabled" <| 1988 \_ -> 1989 Common.init "/" 1990 |> Application.handleCallback 1991 (Callback.AllJobsFetched <| Data.httpNotImplemented) 1992 |> Tuple.first 1993 |> Application.handleDelivery 1994 (ClockTicked FiveSeconds <| 1995 Time.millisToPosix 0 1996 ) 1997 |> Tuple.second 1998 |> Common.notContains Effects.FetchAllJobs 1999 , test "auto refreshes jobs on next five-second tick after dropping" <| 2000 \_ -> 2001 Common.init "/" 2002 |> Application.handleCallback 2003 (Callback.AllJobsFetched <| Ok []) 2004 |> Tuple.first 2005 |> Application.update 2006 (ApplicationMsgs.Update <| Msgs.DragStart "team" "pipeline") 2007 |> Tuple.first 2008 |> Application.handleDelivery 2009 (ClockTicked FiveSeconds <| 2010 Time.millisToPosix 0 2011 ) 2012 |> Tuple.first 2013 |> Application.update 2014 (ApplicationMsgs.Update <| Msgs.DragEnd) 2015 |> Tuple.first 2016 |> Application.handleDelivery 2017 (ClockTicked FiveSeconds <| 2018 Time.millisToPosix 0 2019 ) 2020 |> Tuple.second 2021 |> Common.contains Effects.FetchAllJobs 2022 , test "don't fetch any jobs until the first request finishes" <| 2023 \_ -> 2024 Common.init "/" 2025 |> Application.handleDelivery 2026 (ClockTicked FiveSeconds <| Time.millisToPosix 0) 2027 |> Tuple.second 2028 |> Common.notContains Effects.FetchAllJobs 2029 , test "don't fetch all jobs until the last request finishes" <| 2030 \_ -> 2031 Common.init "/" 2032 |> Application.handleCallback 2033 (Callback.AllJobsFetched <| Ok []) 2034 |> Tuple.first 2035 |> Application.handleDelivery 2036 (ClockTicked FiveSeconds <| Time.millisToPosix 0) 2037 |> Tuple.first 2038 |> Application.handleDelivery 2039 (ClockTicked FiveSeconds <| Time.millisToPosix 0) 2040 |> Tuple.second 2041 |> Common.notContains Effects.FetchAllJobs 2042 , test "navigate to non-hd view on logged out when in non-hd view" <| 2043 \_ -> 2044 Common.init "/" 2045 |> Application.handleCallback 2046 (Callback.LoggedOut <| Ok ()) 2047 |> Tuple.second 2048 |> Common.contains (Effects.NavigateTo "/") 2049 , test "navigate to hd view on logged out when in hd view" <| 2050 \_ -> 2051 Common.init "/hd" 2052 |> Application.handleCallback 2053 (Callback.LoggedOut <| Ok ()) 2054 |> Tuple.second 2055 |> Common.contains (Effects.NavigateTo "/hd") 2056 , test "fetch all teams on logged out" <| 2057 \_ -> 2058 Common.init "/" 2059 |> Application.handleCallback 2060 (Callback.LoggedOut <| Ok ()) 2061 |> Tuple.second 2062 |> Common.contains Effects.FetchAllTeams 2063 , test "fetch all resources on logged out" <| 2064 \_ -> 2065 Common.init "/" 2066 |> Application.handleCallback 2067 (Callback.LoggedOut <| Ok ()) 2068 |> Tuple.second 2069 |> Common.contains Effects.FetchAllResources 2070 , test "fetch all jobs on logged out" <| 2071 \_ -> 2072 Common.init "/" 2073 |> Application.handleCallback 2074 (Callback.LoggedOut <| Ok ()) 2075 |> Tuple.second 2076 |> Common.contains Effects.FetchAllJobs 2077 , test "fetch all pipelines on logged out" <| 2078 \_ -> 2079 Common.init "/" 2080 |> Application.handleCallback 2081 (Callback.LoggedOut <| Ok ()) 2082 |> Tuple.second 2083 |> Common.contains Effects.FetchAllPipelines 2084 ] 2085 2086 2087 afterSeconds : Int -> Application.Model -> Application.Model 2088 afterSeconds n = 2089 List.repeat n 2090 (Application.update 2091 (ApplicationMsgs.DeliveryReceived <| 2092 ClockTicked OneSecond <| 2093 Time.millisToPosix 1000 2094 ) 2095 >> Tuple.first 2096 ) 2097 |> List.foldr (>>) identity 2098 2099 2100 csrfToken : String 2101 csrfToken = 2102 "csrf_token" 2103 2104 2105 iconSelector : { size : String, image : Assets.Asset } -> List Selector 2106 iconSelector { size, image } = 2107 [ style "background-image" <| Assets.backgroundImage <| Just image 2108 , style "background-position" "50% 50%" 2109 , style "background-repeat" "no-repeat" 2110 , style "width" size 2111 , style "height" size 2112 ] 2113 2114 2115 whenOnDashboard : { highDensity : Bool } -> Application.Model 2116 whenOnDashboard { highDensity } = 2117 Common.init 2118 (if highDensity then 2119 "/hd" 2120 2121 else 2122 "/" 2123 ) 2124 |> Application.handleCallback 2125 (Callback.GotViewport Msgs.Dashboard <| 2126 Ok <| 2127 { scene = 2128 { width = 600 2129 , height = 600 2130 } 2131 , viewport = 2132 { width = 600 2133 , height = 600 2134 , x = 0 2135 , y = 0 2136 } 2137 } 2138 ) 2139 |> Tuple.first 2140 2141 2142 whenOnDashboardViewingAllPipelines : { highDensity : Bool } -> Application.Model 2143 whenOnDashboardViewingAllPipelines { highDensity } = 2144 whenOnDashboard { highDensity = highDensity } 2145 |> Application.handleDelivery 2146 (RouteChanged <| 2147 Routes.Dashboard 2148 { searchType = 2149 if highDensity then 2150 Routes.HighDensity 2151 2152 else 2153 Routes.Normal "" 2154 , dashboardView = Routes.ViewAllPipelines 2155 } 2156 ) 2157 |> Tuple.first 2158 2159 2160 givenDataAndUser : 2161 List Concourse.Team 2162 -> Concourse.User 2163 -> Application.Model 2164 -> ( Application.Model, List Effects.Effect ) 2165 givenDataAndUser data user = 2166 Application.handleCallback 2167 (Callback.AllTeamsFetched <| Ok data) 2168 >> Tuple.first 2169 >> Application.handleCallback (Callback.UserFetched <| Ok user) 2170 2171 2172 userWithRoles : List ( String, List String ) -> Concourse.User 2173 userWithRoles roles = 2174 { id = "0" 2175 , userName = "test" 2176 , name = "test" 2177 , email = "test" 2178 , isAdmin = False 2179 , teams = 2180 Dict.fromList roles 2181 } 2182 2183 2184 givenDataUnauthenticated : 2185 List Concourse.Team 2186 -> Application.Model 2187 -> ( Application.Model, List Effects.Effect ) 2188 givenDataUnauthenticated data = 2189 Application.handleCallback 2190 (Callback.AllTeamsFetched <| Ok data) 2191 >> Tuple.first 2192 >> Application.handleCallback 2193 (Callback.UserFetched <| Data.httpUnauthorized) 2194 2195 2196 givenClusterInfo : 2197 String 2198 -> String 2199 -> Application.Model 2200 -> ( Application.Model, List Effects.Effect ) 2201 givenClusterInfo version clusterName = 2202 Application.handleCallback 2203 (Callback.ClusterInfoFetched <| 2204 Ok { version = version, clusterName = clusterName } 2205 ) 2206 2207 2208 apiData : List ( String, List String ) -> List Concourse.Team 2209 apiData pipelines = 2210 pipelines |> List.map Tuple.first |> List.indexedMap Concourse.Team 2211 2212 2213 running : Concourse.Job -> Concourse.Job 2214 running j = 2215 { j 2216 | nextBuild = 2217 Just 2218 { id = 1 2219 , name = "1" 2220 , job = Just Data.jobId 2221 , status = BuildStatusStarted 2222 , duration = 2223 { startedAt = Nothing 2224 , finishedAt = Nothing 2225 } 2226 , reapTime = Nothing 2227 } 2228 } 2229 2230 2231 otherJob : BuildStatus -> Concourse.Job 2232 otherJob = 2233 jobWithNameTransitionedAt "other-job" <| Just <| Time.millisToPosix 0 2234 2235 2236 job : BuildStatus -> Concourse.Job 2237 job = 2238 jobWithNameTransitionedAt "job" <| Just <| Time.millisToPosix 0 2239 2240 2241 jobWithNameTransitionedAt : String -> Maybe Time.Posix -> BuildStatus -> Concourse.Job 2242 jobWithNameTransitionedAt jobName transitionedAt status = 2243 { name = jobName 2244 , pipelineName = "pipeline" 2245 , teamName = "team" 2246 , nextBuild = Nothing 2247 , finishedBuild = 2248 Just 2249 { id = 0 2250 , name = "0" 2251 , job = Just Data.jobId 2252 , status = status 2253 , duration = 2254 { startedAt = Nothing 2255 , finishedAt = Nothing 2256 } 2257 , reapTime = Nothing 2258 } 2259 , transitionBuild = 2260 transitionedAt 2261 |> Maybe.map 2262 (\t -> 2263 { id = 1 2264 , name = "1" 2265 , job = Just Data.jobId 2266 , status = status 2267 , duration = 2268 { startedAt = Nothing 2269 , finishedAt = Just <| t 2270 } 2271 , reapTime = Nothing 2272 } 2273 ) 2274 , paused = False 2275 , disableManualTrigger = False 2276 , inputs = [] 2277 , outputs = [] 2278 , groups = [] 2279 } 2280 2281 2282 circularJobs : List Concourse.Job 2283 circularJobs = 2284 [ { name = "jobA" 2285 , pipelineName = "pipeline" 2286 , teamName = "team" 2287 , nextBuild = Nothing 2288 , finishedBuild = 2289 Just 2290 { id = 0 2291 , name = "0" 2292 , job = Just (Data.jobId |> Data.withJobName "jobA") 2293 , status = BuildStatusSucceeded 2294 , duration = 2295 { startedAt = Nothing 2296 , finishedAt = Nothing 2297 } 2298 , reapTime = Nothing 2299 } 2300 , transitionBuild = 2301 Just 2302 { id = 1 2303 , name = "1" 2304 , job = Just (Data.jobId |> Data.withJobName "jobA") 2305 , status = BuildStatusSucceeded 2306 , duration = 2307 { startedAt = Nothing 2308 , finishedAt = Just <| Time.millisToPosix 0 2309 } 2310 , reapTime = Nothing 2311 } 2312 , paused = False 2313 , disableManualTrigger = False 2314 , inputs = 2315 [ { name = "inA" 2316 , resource = "res0" 2317 , passed = [ "jobB" ] 2318 , trigger = True 2319 } 2320 ] 2321 , outputs = [] 2322 , groups = [] 2323 } 2324 , { name = "jobB" 2325 , pipelineName = "pipeline" 2326 , teamName = "team" 2327 , nextBuild = Nothing 2328 , finishedBuild = 2329 Just 2330 { id = 0 2331 , name = "0" 2332 , job = Just (Data.jobId |> Data.withJobName "jobB") 2333 , status = BuildStatusSucceeded 2334 , duration = 2335 { startedAt = Nothing 2336 , finishedAt = Nothing 2337 } 2338 , reapTime = Nothing 2339 } 2340 , transitionBuild = 2341 Just 2342 { id = 1 2343 , name = "1" 2344 , job = Just (Data.jobId |> Data.withJobName "jobB") 2345 , status = BuildStatusSucceeded 2346 , duration = 2347 { startedAt = Nothing 2348 , finishedAt = Just <| Time.millisToPosix 0 2349 } 2350 , reapTime = Nothing 2351 } 2352 , paused = False 2353 , disableManualTrigger = False 2354 , inputs = 2355 [ { name = "inB" 2356 , resource = "res0" 2357 , passed = [ "jobA" ] 2358 , trigger = True 2359 } 2360 ] 2361 , outputs = [] 2362 , groups = [] 2363 } 2364 ] 2365 2366 2367 teamHeaderSelector : List Selector 2368 teamHeaderSelector = 2369 [ class <| .sectionHeaderClass Effects.stickyHeaderConfig ] 2370 2371 2372 teamHeaderHasNoPill : 2373 String 2374 -> Query.Single ApplicationMsgs.TopLevelMessage 2375 -> Expectation 2376 teamHeaderHasNoPill teamName = 2377 Query.find (teamHeaderSelector ++ [ containing [ text teamName ] ]) 2378 >> Query.children [] 2379 >> Query.count (Expect.equal 1) 2380 2381 2382 teamHeaderHasPill : 2383 String 2384 -> String 2385 -> Query.Single ApplicationMsgs.TopLevelMessage 2386 -> Expectation 2387 teamHeaderHasPill teamName pillText = 2388 Query.find (teamHeaderSelector ++ [ containing [ text teamName ] ]) 2389 >> Query.children [] 2390 >> Expect.all 2391 [ Query.count (Expect.equal 2) 2392 , Query.index 1 >> Query.has [ text pillText ] 2393 ]