github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/web/elm/tests/ResourceTests.elm (about)

     1  module ResourceTests exposing (all)
     2  
     3  import Application.Application as Application
     4  import Application.Models exposing (Session)
     5  import Assets
     6  import Build.StepTree.Models as STModels
     7  import ColorValues
     8  import Common exposing (defineHoverBehaviour, queryView)
     9  import Concourse
    10  import Concourse.BuildStatus exposing (BuildStatus(..))
    11  import Concourse.Pagination exposing (Direction(..), Page, Pagination)
    12  import DashboardTests
    13      exposing
    14          ( almostBlack
    15          , iconSelector
    16          , middleGrey
    17          )
    18  import Data
    19  import Dict
    20  import Expect exposing (..)
    21  import HoverState
    22  import Html.Attributes as Attr
    23  import Keyboard
    24  import Message.Callback as Callback exposing (Callback(..))
    25  import Message.Effects as Effects
    26  import Message.Message exposing (DomID(..), Message(..))
    27  import Message.Subscription as Subscription
    28      exposing
    29          ( Delivery(..)
    30          , Interval(..)
    31          )
    32  import Message.TopLevelMessage as Msgs
    33  import Pinned exposing (VersionPinState(..))
    34  import RemoteData
    35  import Resource.Models as Models
    36  import Resource.Resource as Resource
    37  import Routes
    38  import ScreenSize
    39  import Set
    40  import Test exposing (..)
    41  import Test.Html.Event as Event
    42  import Test.Html.Query as Query
    43  import Test.Html.Selector
    44      exposing
    45          ( Selector
    46          , attribute
    47          , class
    48          , containing
    49          , id
    50          , style
    51          , tag
    52          , text
    53          )
    54  import Time
    55  import Url
    56  import UserState exposing (UserState(..))
    57  import Views.Styles
    58  
    59  
    60  teamName : String
    61  teamName =
    62      Data.teamName
    63  
    64  
    65  pipelineName : String
    66  pipelineName =
    67      Data.pipelineName
    68  
    69  
    70  resourceName : String
    71  resourceName =
    72      Data.resourceName
    73  
    74  
    75  resourceIcon : String
    76  resourceIcon =
    77      "some-icon"
    78  
    79  
    80  versionID : Models.VersionId
    81  versionID =
    82      Data.resourceVersionId 1
    83  
    84  
    85  otherVersionID : Models.VersionId
    86  otherVersionID =
    87      Data.resourceVersionId 2
    88  
    89  
    90  disabledVersionID : Models.VersionId
    91  disabledVersionID =
    92      Data.resourceVersionId 3
    93  
    94  
    95  version : String
    96  version =
    97      "v1"
    98  
    99  
   100  otherVersion : String
   101  otherVersion =
   102      "v2"
   103  
   104  
   105  disabledVersion : String
   106  disabledVersion =
   107      "v3"
   108  
   109  
   110  purpleHex : String
   111  purpleHex =
   112      "#5c3bd1"
   113  
   114  
   115  lightGreyHex : String
   116  lightGreyHex =
   117      "#3d3c3c"
   118  
   119  
   120  tooltipGreyHex : String
   121  tooltipGreyHex =
   122      "#9b9b9b"
   123  
   124  
   125  darkGreyHex : String
   126  darkGreyHex =
   127      "#1e1d1d"
   128  
   129  
   130  failureRed : String
   131  failureRed =
   132      "#ed4b35"
   133  
   134  
   135  all : Test
   136  all =
   137      describe "resource page"
   138          [ describe "when logging out" <|
   139              let
   140                  loggingOut : () -> ( Application.Model, List Effects.Effect )
   141                  loggingOut _ =
   142                      init
   143                          |> Application.handleCallback
   144                              (Callback.UserFetched <|
   145                                  Ok
   146                                      { id = "test"
   147                                      , userName = "test"
   148                                      , name = "test"
   149                                      , email = "test"
   150                                      , isAdmin = False
   151                                      , teams =
   152                                          Dict.fromList
   153                                              [ ( teamName, [ "member" ] )
   154                                              ]
   155                                      }
   156                              )
   157                          |> Tuple.first
   158                          |> Application.handleCallback
   159                              (Callback.LoggedOut (Ok ()))
   160              in
   161              [ test "updates top bar state" <|
   162                  loggingOut
   163                      >> Tuple.first
   164                      >> queryView
   165                      >> Query.find [ id "top-bar-app" ]
   166                      >> Query.children []
   167                      >> Query.index -1
   168                      >> Query.has [ text "login" ]
   169              , test "redirects to dashboard" <|
   170                  loggingOut
   171                      >> Tuple.second
   172                      >> Expect.equal
   173                          [ Effects.NavigateTo <|
   174                              Routes.toString <|
   175                                  Routes.Dashboard <|
   176                                      { searchType = Routes.Normal ""
   177                                      , dashboardView = Routes.ViewNonArchivedPipelines
   178                                      }
   179                          ]
   180              ]
   181          , test "has title with resource name" <|
   182              \_ ->
   183                  init
   184                      |> Application.view
   185                      |> .title
   186                      |> Expect.equal "resource - Concourse"
   187          , test "fetches time zone on page load" <|
   188              \_ ->
   189                  Application.init
   190                      { turbulenceImgSrc = ""
   191                      , notFoundImgSrc = "notfound.svg"
   192                      , csrfToken = "csrf_token"
   193                      , authToken = ""
   194                      , pipelineRunningKeyframes = "pipeline-running"
   195                      }
   196                      { protocol = Url.Http
   197                      , host = ""
   198                      , port_ = Nothing
   199                      , path =
   200                          "/teams/"
   201                              ++ teamName
   202                              ++ "/pipelines/"
   203                              ++ pipelineName
   204                              ++ "/resources/"
   205                              ++ resourceName
   206                      , query = Nothing
   207                      , fragment = Nothing
   208                      }
   209                      |> Tuple.second
   210                      |> Common.contains Effects.GetCurrentTimeZone
   211          , test "subscribes to the five second interval" <|
   212              \_ ->
   213                  init
   214                      |> Application.subscriptions
   215                      |> Common.contains (Subscription.OnClockTick FiveSeconds)
   216          , test "autorefreshes resource and versions every five seconds" <|
   217              \_ ->
   218                  init
   219                      |> Application.update
   220                          (Msgs.DeliveryReceived
   221                              (ClockTicked FiveSeconds <|
   222                                  Time.millisToPosix 0
   223                              )
   224                          )
   225                      |> Tuple.second
   226                      |> Expect.all
   227                          [ Common.contains (Effects.FetchResource Data.resourceId)
   228                          , Common.contains
   229                              (Effects.FetchVersionedResources Data.resourceId
   230                                  Resource.startingPage
   231                              )
   232                          ]
   233          , test "autorefresh respects expanded state" <|
   234              \_ ->
   235                  init
   236                      |> givenResourceIsNotPinned
   237                      |> givenVersionsWithoutPagination
   238                      |> update
   239                          (Message.Message.Click <|
   240                              Message.Message.VersionHeader versionID
   241                          )
   242                      |> Tuple.first
   243                      |> givenVersionsWithoutPagination
   244                      |> queryView
   245                      |> Query.find (versionSelector version)
   246                      |> Query.has [ text "metadata" ]
   247          , test "autorefresh respects 'Inputs To'" <|
   248              \_ ->
   249                  init
   250                      |> givenResourceIsNotPinned
   251                      |> givenVersionsWithoutPagination
   252                      |> update
   253                          (Message.Message.Click <|
   254                              Message.Message.VersionHeader versionID
   255                          )
   256                      |> Tuple.first
   257                      |> Application.handleCallback
   258                          (Callback.InputToFetched
   259                              (Ok
   260                                  ( versionID
   261                                  , [ { id = 0
   262                                      , name = "some-build"
   263                                      , job = Just Data.jobId
   264                                      , status = BuildStatusSucceeded
   265                                      , duration =
   266                                          { startedAt = Nothing
   267                                          , finishedAt = Nothing
   268                                          }
   269                                      , reapTime = Nothing
   270                                      }
   271                                    ]
   272                                  )
   273                              )
   274                          )
   275                      |> Tuple.first
   276                      |> givenVersionsWithoutPagination
   277                      |> queryView
   278                      |> Query.find (versionSelector version)
   279                      |> Query.has [ text "some-build" ]
   280          , test "autorefresh respects 'Outputs Of'" <|
   281              \_ ->
   282                  init
   283                      |> givenResourceIsNotPinned
   284                      |> givenVersionsWithoutPagination
   285                      |> update
   286                          (Message.Message.Click <|
   287                              Message.Message.VersionHeader versionID
   288                          )
   289                      |> Tuple.first
   290                      |> Application.handleCallback
   291                          (Callback.OutputOfFetched
   292                              (Ok
   293                                  ( versionID
   294                                  , [ { id = 0
   295                                      , name = "some-build"
   296                                      , job = Just Data.jobId
   297                                      , status = BuildStatusSucceeded
   298                                      , duration =
   299                                          { startedAt = Nothing
   300                                          , finishedAt = Nothing
   301                                          }
   302                                      , reapTime = Nothing
   303                                      }
   304                                    ]
   305                                  )
   306                              )
   307                          )
   308                      |> Tuple.first
   309                      |> givenVersionsWithoutPagination
   310                      |> queryView
   311                      |> Query.find (versionSelector version)
   312                      |> Query.has [ text "some-build" ]
   313          , describe "page header with icon" <|
   314              let
   315                  pageHeader =
   316                      init
   317                          |> givenResourceHasIcon
   318                          |> queryView
   319                          |> Query.find [ id "page-header" ]
   320              in
   321              [ describe "resource name"
   322                  [ test "on the left is the resource name" <|
   323                      \_ ->
   324                          pageHeader
   325                              |> Query.children []
   326                              |> Query.index 0
   327                              |> Query.has [ tag "svg", text resourceName, tag "h1" ]
   328                  ]
   329              , describe "resource icon"
   330                  [ test "on the left is the resource icon" <|
   331                      \_ ->
   332                          pageHeader
   333                              |> Query.children []
   334                              |> Query.index 0
   335                              |> Query.has [ tag "svg" ]
   336                  ]
   337              ]
   338          , describe "page header" <|
   339              let
   340                  pageHeader =
   341                      init
   342                          |> givenResourceIsNotPinned
   343                          |> queryView
   344                          |> Query.find [ id "page-header" ]
   345              in
   346              [ test "has dark grey background" <|
   347                  \_ ->
   348                      pageHeader
   349                          |> Query.has
   350                              [ style "height" "60px"
   351                              , style "background-color" "#2a2929"
   352                              ]
   353              , test "lays out contents horizontally, stretching them vertically" <|
   354                  \_ ->
   355                      pageHeader
   356                          |> Query.has
   357                              [ style "display" "flex"
   358                              , style "align-items" "stretch"
   359                              ]
   360              , describe "resource name"
   361                  [ test "on the left is the resource name" <|
   362                      \_ ->
   363                          pageHeader
   364                              |> Query.children []
   365                              |> Query.index 0
   366                              |> Query.has [ text resourceName, tag "h1" ]
   367                  , test "the text is vertically centered" <|
   368                      \_ ->
   369                          pageHeader
   370                              |> Query.children []
   371                              |> Query.index 0
   372                              |> Query.has
   373                                  [ style "margin-left" "18px"
   374                                  , style "display" "flex"
   375                                  , style "align-items" "center"
   376                                  , style "justify-content" "center"
   377                                  ]
   378                  ]
   379              , describe "last checked"
   380                  [ test "last checked view is second from left" <|
   381                      \_ ->
   382                          init
   383                              |> givenResourceIsNotPinned
   384                              |> Application.update
   385                                  (Msgs.DeliveryReceived <|
   386                                      Subscription.ClockTicked Subscription.OneSecond <|
   387                                          Time.millisToPosix 1000
   388                                  )
   389                              |> Tuple.first
   390                              |> queryView
   391                              |> Query.find [ id "page-header" ]
   392                              |> Query.children []
   393                              |> Query.index 1
   394                              |> Query.has [ text "1s ago" ]
   395                  , test "last checked view displays its contents centred" <|
   396                      \_ ->
   397                          init
   398                              |> givenResourceIsNotPinned
   399                              |> Application.update
   400                                  (Msgs.DeliveryReceived <|
   401                                      Subscription.ClockTicked Subscription.OneSecond <|
   402                                          Time.millisToPosix 1000
   403                                  )
   404                              |> Tuple.first
   405                              |> queryView
   406                              |> Query.find [ id "page-header" ]
   407                              |> Query.children []
   408                              |> Query.index 1
   409                              |> Query.has
   410                                  [ style "display" "flex"
   411                                  , style "align-items" "center"
   412                                  , style "justify-content" "center"
   413                                  , style "margin-left" "24px"
   414                                  ]
   415                  , test "does not appear when pipeline is archived" <|
   416                      \_ ->
   417                          init
   418                              |> givenResourceIsNotPinned
   419                              |> givenThePipelineIsArchived
   420                              |> Application.update
   421                                  (Msgs.DeliveryReceived <|
   422                                      Subscription.ClockTicked Subscription.OneSecond <|
   423                                          Time.millisToPosix 1000
   424                                  )
   425                              |> Tuple.first
   426                              |> queryView
   427                              |> Query.hasNot [ id "last-checked" ]
   428                  ]
   429              , describe "pagination"
   430                  [ test "pagination is last on the right" <|
   431                      \_ ->
   432                          init
   433                              |> givenResourceIsNotPinned
   434                              |> givenVersionsWithPagination
   435                              |> queryView
   436                              |> Query.find [ id "page-header" ]
   437                              |> Query.children []
   438                              |> Query.index -1
   439                              |> Query.has [ id "pagination" ]
   440                  , test "pagination displays the pages horizontally" <|
   441                      \_ ->
   442                          init
   443                              |> givenResourceIsNotPinned
   444                              |> givenVersionsWithPagination
   445                              |> queryView
   446                              |> Query.find [ id "pagination" ]
   447                              |> Query.has
   448                                  [ style "display" "flex"
   449                                  , style "align-items" "stretch"
   450                                  ]
   451                  , describe "pagination chevrons"
   452                      [ test "with no pages" <|
   453                          \_ ->
   454                              init
   455                                  |> givenResourceIsNotPinned
   456                                  |> givenVersionsWithoutPagination
   457                                  |> queryView
   458                                  |> Query.find [ id "pagination" ]
   459                                  |> Query.children []
   460                                  |> Expect.all
   461                                      [ Query.index 0
   462                                          >> Query.has
   463                                              [ style "padding" "5px"
   464                                              , style "display" "flex"
   465                                              , style "align-items" "center"
   466                                              , style "border-left" <|
   467                                                  "1px solid "
   468                                                      ++ middleGrey
   469                                              , containing
   470                                                  (iconSelector
   471                                                      { image = Assets.ChevronLeft
   472                                                      , size = "24px"
   473                                                      }
   474                                                      ++ [ style "padding" "5px"
   475                                                         , style "opacity" "0.5"
   476                                                         ]
   477                                                  )
   478                                              ]
   479                                      , Query.index 1
   480                                          >> Query.has
   481                                              [ style "padding" "5px"
   482                                              , style "display" "flex"
   483                                              , style "align-items" "center"
   484                                              , style "border-left" <|
   485                                                  "1px solid "
   486                                                      ++ middleGrey
   487                                              , containing
   488                                                  (iconSelector
   489                                                      { image = Assets.ChevronRight
   490                                                      , size = "24px"
   491                                                      }
   492                                                      ++ [ style "padding" "5px"
   493                                                         , style "opacity" "0.5"
   494                                                         ]
   495                                                  )
   496                                              ]
   497                                      ]
   498                      , defineHoverBehaviour <|
   499                          let
   500                              urlPath =
   501                                  "/teams/team/pipelines/pipeline/resources/resource?from=100&limit=1"
   502                          in
   503                          { name = "left pagination chevron with previous page"
   504                          , setup =
   505                              init
   506                                  |> givenResourceIsNotPinned
   507                                  |> givenVersionsWithPagination
   508                          , query =
   509                              queryView
   510                                  >> Query.find [ id "pagination" ]
   511                                  >> Query.children []
   512                                  >> Query.index 0
   513                          , unhoveredSelector =
   514                              { description = "white left chevron"
   515                              , selector =
   516                                  [ style "padding" "5px"
   517                                  , style "display" "flex"
   518                                  , style "align-items" "center"
   519                                  , style "border-left" <|
   520                                      "1px solid "
   521                                          ++ middleGrey
   522                                  , containing
   523                                      (iconSelector
   524                                          { image = Assets.ChevronLeft
   525                                          , size = "24px"
   526                                          }
   527                                          ++ [ style "padding" "5px"
   528                                             , style "opacity" "1"
   529                                             , attribute <| Attr.href urlPath
   530                                             ]
   531                                      )
   532                                  ]
   533                              }
   534                          , hoveredSelector =
   535                              { description =
   536                                  "left chevron with light grey circular bg"
   537                              , selector =
   538                                  [ style "padding" "5px"
   539                                  , style "display" "flex"
   540                                  , style "align-items" "center"
   541                                  , style "border-left" <|
   542                                      "1px solid "
   543                                          ++ middleGrey
   544                                  , containing
   545                                      (iconSelector
   546                                          { image = Assets.ChevronLeft
   547                                          , size = "24px"
   548                                          }
   549                                          ++ [ style "padding" "5px"
   550                                             , style "opacity" "1"
   551                                             , style "border-radius" "50%"
   552                                             , style "background-color" <|
   553                                                  "#504b4b"
   554                                             , attribute <| Attr.href urlPath
   555                                             ]
   556                                      )
   557                                  ]
   558                              }
   559                          , hoverable = Message.Message.PreviousPageButton
   560                          }
   561                      , test "pagination previous loads most recent if less than 100 entries" <|
   562                          \_ ->
   563                              let
   564                                  previousPage =
   565                                      { direction = From 1, limit = 100 }
   566                              in
   567                              init
   568                                  |> givenResourceIsNotPinned
   569                                  |> givenVersionsWithPages previousPage emptyPagination
   570                                  |> Tuple.second
   571                                  |> Common.contains
   572                                      (Effects.FetchVersionedResources
   573                                          { teamName = teamName
   574                                          , pipelineName = pipelineName
   575                                          , resourceName = resourceName
   576                                          }
   577                                          Resource.startingPage
   578                                      )
   579                      ]
   580                  ]
   581              ]
   582          , describe "page body" <|
   583              [ test "has 10px padding" <|
   584                  \_ ->
   585                      init
   586                          |> givenResourceIsNotPinned
   587                          |> queryView
   588                          |> Query.find [ id "body" ]
   589                          |> Query.has [ style "padding" "10px" ]
   590              , test "scrolls independently" <|
   591                  \_ ->
   592                      init
   593                          |> givenResourceIsNotPinned
   594                          |> queryView
   595                          |> Query.find [ id "body" ]
   596                          |> Query.has [ style "overflow-y" "auto" ]
   597              , test "fills vertical space" <|
   598                  \_ ->
   599                      init
   600                          |> givenResourceIsNotPinned
   601                          |> queryView
   602                          |> Query.find [ id "body" ]
   603                          |> Query.has [ style "flex-grow" "1" ]
   604              ]
   605          , describe "checkboxes" <|
   606              let
   607                  checkIcon =
   608                      Assets.backgroundImage <|
   609                          Just Assets.CheckmarkIcon
   610              in
   611              [ test "there is a checkbox for every version" <|
   612                  \_ ->
   613                      init
   614                          |> givenResourceIsNotPinned
   615                          |> givenVersionsWithoutPagination
   616                          |> queryView
   617                          |> Query.find [ class "resource-versions" ]
   618                          |> Query.findAll anyVersionSelector
   619                          |> Query.each hasCheckbox
   620              , test "there is a pointer cursor for every checkbox" <|
   621                  \_ ->
   622                      init
   623                          |> givenResourceIsNotPinned
   624                          |> givenVersionsWithoutPagination
   625                          |> queryView
   626                          |> Query.find [ class "resource-versions" ]
   627                          |> Query.findAll anyVersionSelector
   628                          |> Query.each
   629                              (Query.find checkboxSelector
   630                                  >> Query.has pointerCursor
   631                              )
   632              , test "enabled versions have checkmarks" <|
   633                  \_ ->
   634                      init
   635                          |> givenResourcePinnedStatically
   636                          |> givenVersionsWithoutPagination
   637                          |> queryView
   638                          |> Expect.all
   639                              [ Query.find (versionSelector version)
   640                                  >> Query.find checkboxSelector
   641                                  >> Query.has
   642                                      [ style "background-image" checkIcon ]
   643                              , Query.find (versionSelector otherVersion)
   644                                  >> Query.find checkboxSelector
   645                                  >> Query.has
   646                                      [ style "background-image" checkIcon ]
   647                              ]
   648              , test "disabled versions do not have checkmarks" <|
   649                  \_ ->
   650                      init
   651                          |> givenResourcePinnedStatically
   652                          |> givenVersionsWithoutPagination
   653                          |> queryView
   654                          |> Query.find (versionSelector disabledVersion)
   655                          |> Query.find checkboxSelector
   656                          |> Query.hasNot [ style "background-image" checkIcon ]
   657              , test "clicking the checkbox on an enabled version triggers a ToggleVersion msg" <|
   658                  \_ ->
   659                      init
   660                          |> givenResourcePinnedStatically
   661                          |> givenVersionsWithoutPagination
   662                          |> queryView
   663                          |> Query.find (versionSelector version)
   664                          |> Query.find checkboxSelector
   665                          |> Event.simulate Event.click
   666                          |> Event.expect
   667                              (Msgs.Update <|
   668                                  Message.Message.Click <|
   669                                      Message.Message.VersionToggle
   670                                          versionID
   671                              )
   672              , test "receiving a (ToggleVersion Disable) msg causes the relevant checkbox to go into a transition state" <|
   673                  \_ ->
   674                      init
   675                          |> givenResourcePinnedStatically
   676                          |> givenVersionsWithoutPagination
   677                          |> clickToDisable versionID
   678                          |> queryView
   679                          |> Query.find (versionSelector version)
   680                          |> Query.find checkboxSelector
   681                          |> checkboxHasTransitionState
   682              , test "autorefreshing after receiving a ToggleVersion msg causes the relevant checkbox to stay in a transition state" <|
   683                  \_ ->
   684                      init
   685                          |> givenResourcePinnedStatically
   686                          |> givenVersionsWithoutPagination
   687                          |> clickToDisable versionID
   688                          |> givenVersionsWithoutPagination
   689                          |> queryView
   690                          |> Query.find (versionSelector version)
   691                          |> Query.find checkboxSelector
   692                          |> checkboxHasTransitionState
   693              , test "receiving a successful VersionToggled msg causes the relevant checkbox to appear unchecked" <|
   694                  \_ ->
   695                      init
   696                          |> givenResourcePinnedStatically
   697                          |> givenVersionsWithoutPagination
   698                          |> clickToDisable versionID
   699                          |> Application.handleCallback (Callback.VersionToggled Message.Message.Disable versionID (Ok ()))
   700                          |> Tuple.first
   701                          |> queryView
   702                          |> Query.find (versionSelector version)
   703                          |> versionHasDisabledState
   704              , test "receiving an error on VersionToggled msg causes the checkbox to go back to its checked state" <|
   705                  \_ ->
   706                      init
   707                          |> givenResourcePinnedStatically
   708                          |> givenVersionsWithoutPagination
   709                          |> clickToDisable versionID
   710                          |> Application.handleCallback
   711                              (Callback.VersionToggled
   712                                  Message.Message.Disable
   713                                  versionID
   714                                  Data.httpInternalServerError
   715                              )
   716                          |> Tuple.first
   717                          |> queryView
   718                          |> Query.find (versionSelector version)
   719                          |> Query.find checkboxSelector
   720                          |> checkboxHasEnabledState
   721              , test "clicking the checkbox on a disabled version triggers a ToggleVersion msg" <|
   722                  \_ ->
   723                      init
   724                          |> givenResourcePinnedStatically
   725                          |> givenVersionsWithoutPagination
   726                          |> queryView
   727                          |> Query.find (versionSelector disabledVersion)
   728                          |> Query.find checkboxSelector
   729                          |> Event.simulate Event.click
   730                          |> Event.expect
   731                              (Msgs.Update <|
   732                                  Message.Message.Click <|
   733                                      Message.Message.VersionToggle
   734                                          disabledVersionID
   735                              )
   736              , test "receiving a (ToggleVersion Enable) msg causes the relevant checkbox to go into a transition state" <|
   737                  \_ ->
   738                      init
   739                          |> givenResourcePinnedStatically
   740                          |> givenVersionsWithoutPagination
   741                          |> Application.update
   742                              (Msgs.Update <|
   743                                  Message.Message.Click <|
   744                                      Message.Message.VersionToggle
   745                                          disabledVersionID
   746                              )
   747                          |> Tuple.first
   748                          |> queryView
   749                          |> Query.find (versionSelector disabledVersion)
   750                          |> Query.find checkboxSelector
   751                          |> checkboxHasTransitionState
   752              , test "receiving a successful VersionToggled msg causes the relevant checkbox to appear checked" <|
   753                  \_ ->
   754                      init
   755                          |> givenResourcePinnedStatically
   756                          |> givenVersionsWithoutPagination
   757                          |> Application.update
   758                              (Msgs.Update <|
   759                                  Message.Message.Click <|
   760                                      Message.Message.VersionToggle
   761                                          disabledVersionID
   762                              )
   763                          |> Tuple.first
   764                          |> Application.handleCallback
   765                              (Callback.VersionToggled
   766                                  Message.Message.Enable
   767                                  disabledVersionID
   768                                  (Ok ())
   769                              )
   770                          |> Tuple.first
   771                          |> queryView
   772                          |> Query.find (versionSelector disabledVersion)
   773                          |> Query.find checkboxSelector
   774                          |> checkboxHasEnabledState
   775              , test "receiving a failing VersionToggled msg causes the relevant checkbox to return to its unchecked state" <|
   776                  \_ ->
   777                      init
   778                          |> givenResourcePinnedStatically
   779                          |> givenVersionsWithoutPagination
   780                          |> Application.update
   781                              (Msgs.Update <|
   782                                  Message.Message.Click <|
   783                                      Message.Message.VersionToggle
   784                                          disabledVersionID
   785                              )
   786                          |> Tuple.first
   787                          |> Application.handleCallback
   788                              (Callback.VersionToggled
   789                                  Message.Message.Enable
   790                                  disabledVersionID
   791                                  Data.httpInternalServerError
   792                              )
   793                          |> Tuple.first
   794                          |> queryView
   795                          |> Query.find (versionSelector disabledVersion)
   796                          |> Query.find checkboxSelector
   797                          |> checkboxHasDisabledState
   798              ]
   799          , describe "given resource is pinned statically"
   800              [ describe "pin bar"
   801                  [ test "then pinned version is visible in pin bar" <|
   802                      \_ ->
   803                          init
   804                              |> givenResourcePinnedStatically
   805                              |> queryView
   806                              |> Query.find [ id "pin-bar" ]
   807                              |> Query.has [ text version ]
   808                  , test "pin icon on pin bar has default cursor" <|
   809                      \_ ->
   810                          init
   811                              |> givenResourcePinnedStatically
   812                              |> queryView
   813                              |> Query.find [ id "pin-icon" ]
   814                              |> Query.has defaultCursor
   815                  , test "clicking pin icon on pin bar does nothing" <|
   816                      \_ ->
   817                          init
   818                              |> givenResourcePinnedStatically
   819                              |> queryView
   820                              |> Query.find [ id "pin-icon" ]
   821                              |> Event.simulate Event.click
   822                              |> Event.toResult
   823                              |> Expect.err
   824                  , test "there is a bit of space around the pin icon" <|
   825                      \_ ->
   826                          init
   827                              |> givenResourcePinnedStatically
   828                              |> queryView
   829                              |> Query.find [ id "pin-icon" ]
   830                              |> Query.has
   831                                  [ style "margin" "4px 5px 5px 5px" ]
   832                  , test "mousing over pin icon does nothing" <|
   833                      \_ ->
   834                          init
   835                              |> givenResourcePinnedStatically
   836                              |> queryView
   837                              |> Query.find [ id "pin-icon" ]
   838                              |> Event.simulate Event.mouseEnter
   839                              |> Event.toResult
   840                              |> Expect.err
   841                  , test "pin button on pinned version has a purple outline" <|
   842                      \_ ->
   843                          init
   844                              |> givenResourcePinnedStatically
   845                              |> givenVersionsWithoutPagination
   846                              |> queryView
   847                              |> Query.find (versionSelector version)
   848                              |> Query.find pinButtonSelector
   849                              |> Query.has purpleOutlineSelector
   850                  , test "checkbox on pinned version has a purple outline" <|
   851                      \_ ->
   852                          init
   853                              |> givenResourcePinnedStatically
   854                              |> givenVersionsWithoutPagination
   855                              |> queryView
   856                              |> Query.find (versionSelector version)
   857                              |> Query.find checkboxSelector
   858                              |> Query.has purpleOutlineSelector
   859                  , test "all pin buttons have default cursor" <|
   860                      \_ ->
   861                          init
   862                              |> givenResourcePinnedStatically
   863                              |> givenVersionsWithoutPagination
   864                              |> queryView
   865                              |> Query.find [ class "resource-versions" ]
   866                              |> Query.findAll anyVersionSelector
   867                              |> Query.each
   868                                  (Query.find pinButtonSelector
   869                                      >> Query.has defaultCursor
   870                                  )
   871                  , test "version header on pinned version has a purple outline" <|
   872                      \_ ->
   873                          init
   874                              |> givenResourcePinnedStatically
   875                              |> givenVersionsWithoutPagination
   876                              |> queryView
   877                              |> Query.find (versionSelector version)
   878                              |> findLast [ tag "div", containing [ text version ] ]
   879                              |> Query.has purpleOutlineSelector
   880                  , test "mousing over pin bar sends TogglePinBarTooltip message" <|
   881                      \_ ->
   882                          init
   883                              |> givenResourcePinnedStatically
   884                              |> queryView
   885                              |> Query.find [ id "pin-bar" ]
   886                              |> Event.simulate Event.mouseEnter
   887                              |> Event.expect
   888                                  (Msgs.Update <| Message.Message.Hover <| Just Message.Message.PinBar)
   889                  , test "TogglePinBarTooltip causes tooltip to appear" <|
   890                      \_ ->
   891                          init
   892                              |> givenResourcePinnedStatically
   893                              |> hoverOverPinBar
   894                              |> queryView
   895                              |> Query.has pinBarTooltipSelector
   896                  , test "pin bar tooltip has text 'pinned in pipeline config'" <|
   897                      \_ ->
   898                          init
   899                              |> givenResourcePinnedStatically
   900                              |> hoverOverPinBar
   901                              |> queryView
   902                              |> Query.find pinBarTooltipSelector
   903                              |> Query.has [ text "pinned in pipeline config" ]
   904                  , test "pin bar tooltip is positioned above and near the left of the pin bar" <|
   905                      \_ ->
   906                          init
   907                              |> givenResourcePinnedStatically
   908                              |> hoverOverPinBar
   909                              |> queryView
   910                              |> Query.find pinBarTooltipSelector
   911                              |> Query.has
   912                                  [ style "position" "absolute"
   913                                  , style "top" "-10px"
   914                                  , style "left" "30px"
   915                                  ]
   916                  , test "pin bar tooltip is light grey" <|
   917                      \_ ->
   918                          init
   919                              |> givenResourcePinnedStatically
   920                              |> hoverOverPinBar
   921                              |> queryView
   922                              |> Query.find pinBarTooltipSelector
   923                              |> Query.has
   924                                  [ style "background-color" ColorValues.grey20 ]
   925                  , test "pin bar tooltip has a bit of padding around text" <|
   926                      \_ ->
   927                          init
   928                              |> givenResourcePinnedStatically
   929                              |> hoverOverPinBar
   930                              |> queryView
   931                              |> Query.find pinBarTooltipSelector
   932                              |> Query.has
   933                                  [ style "padding" "5px" ]
   934                  , test "pin bar tooltip appears above other elements in the DOM" <|
   935                      \_ ->
   936                          init
   937                              |> givenResourcePinnedStatically
   938                              |> hoverOverPinBar
   939                              |> queryView
   940                              |> Query.find pinBarTooltipSelector
   941                              |> Query.has
   942                                  [ style "z-index" "2" ]
   943                  , test "mousing out of pin bar sends Hover Nothing message" <|
   944                      \_ ->
   945                          init
   946                              |> givenResourcePinnedStatically
   947                              |> hoverOverPinBar
   948                              |> queryView
   949                              |> Query.find [ id "pin-bar" ]
   950                              |> Event.simulate Event.mouseLeave
   951                              |> Event.expect
   952                                  (Msgs.Update <| Message.Message.Hover Nothing)
   953                  , test "when mousing off pin bar, tooltip disappears" <|
   954                      \_ ->
   955                          init
   956                              |> givenResourcePinnedStatically
   957                              |> hoverOverPinBar
   958                              |> update (Message.Message.Hover Nothing)
   959                              |> Tuple.first
   960                              |> queryView
   961                              |> Query.hasNot pinBarTooltipSelector
   962                  ]
   963              , describe "per-version pin buttons"
   964                  [ test "unpinned versions are lower opacity" <|
   965                      \_ ->
   966                          init
   967                              |> givenResourcePinnedStatically
   968                              |> givenVersionsWithoutPagination
   969                              |> queryView
   970                              |> Query.find (versionSelector otherVersion)
   971                              |> Query.has [ style "opacity" "0.5" ]
   972                  , test "mousing over the pinned version's pin button sends ToggleVersionTooltip" <|
   973                      \_ ->
   974                          init
   975                              |> givenResourcePinnedStatically
   976                              |> givenVersionsWithoutPagination
   977                              |> queryView
   978                              |> Query.find (versionSelector version)
   979                              |> Query.find pinButtonSelector
   980                              |> Event.simulate Event.mouseOver
   981                              |> Event.expect
   982                                  (Msgs.Update <|
   983                                      Message.Message.Hover <|
   984                                          Just <|
   985                                              Message.Message.PinButton versionID
   986                                  )
   987                  , test "mousing over an unpinned version's pin button doesn't send any msg" <|
   988                      \_ ->
   989                          init
   990                              |> givenResourcePinnedStatically
   991                              |> givenVersionsWithoutPagination
   992                              |> queryView
   993                              |> Query.find (versionSelector otherVersion)
   994                              |> Query.find pinButtonSelector
   995                              |> Event.simulate Event.mouseOver
   996                              |> Event.toResult
   997                              |> Expect.err
   998                  , test "shows tooltip on the pinned version's pin button on ToggleVersionTooltip" <|
   999                      \_ ->
  1000                          init
  1001                              |> givenResourcePinnedStatically
  1002                              |> givenVersionsWithoutPagination
  1003                              |> hoverOverPinButton
  1004                              |> queryView
  1005                              |> Query.find (versionSelector version)
  1006                              |> Query.has versionTooltipSelector
  1007                  , test "keeps tooltip on the pinned version's pin button on autorefresh" <|
  1008                      \_ ->
  1009                          init
  1010                              |> givenResourcePinnedStatically
  1011                              |> givenVersionsWithoutPagination
  1012                              |> hoverOverPinButton
  1013                              |> givenVersionsWithoutPagination
  1014                              |> queryView
  1015                              |> Query.find (versionSelector version)
  1016                              |> Query.has versionTooltipSelector
  1017                  , test "mousing off the pinned version's pin button sends Hover Nothing" <|
  1018                      \_ ->
  1019                          init
  1020                              |> givenResourcePinnedStatically
  1021                              |> givenVersionsWithoutPagination
  1022                              |> hoverOverPinButton
  1023                              |> queryView
  1024                              |> Query.find (versionSelector version)
  1025                              |> Query.find pinButtonSelector
  1026                              |> Event.simulate Event.mouseOut
  1027                              |> Event.expect
  1028                                  (Msgs.Update <| Message.Message.Hover Nothing)
  1029                  , test "mousing off an unpinned version's pin button doesn't send any msg" <|
  1030                      \_ ->
  1031                          init
  1032                              |> givenResourcePinnedStatically
  1033                              |> givenVersionsWithoutPagination
  1034                              |> hoverOverPinButton
  1035                              |> queryView
  1036                              |> Query.find (versionSelector otherVersion)
  1037                              |> Query.find pinButtonSelector
  1038                              |> Event.simulate Event.mouseOut
  1039                              |> Event.toResult
  1040                              |> Expect.err
  1041                  , test "hides tooltip on the pinned version's pin button on ToggleVersionTooltip" <|
  1042                      \_ ->
  1043                          init
  1044                              |> givenResourcePinnedStatically
  1045                              |> givenVersionsWithoutPagination
  1046                              |> hoverOverPinButton
  1047                              |> update (Message.Message.Hover Nothing)
  1048                              |> Tuple.first
  1049                              |> queryView
  1050                              |> Query.find (versionSelector version)
  1051                              |> Query.hasNot versionTooltipSelector
  1052                  , test "clicking on pin button on pinned version doesn't send any msg" <|
  1053                      \_ ->
  1054                          init
  1055                              |> givenResourcePinnedStatically
  1056                              |> givenVersionsWithoutPagination
  1057                              |> clickToUnpin
  1058                              |> queryView
  1059                              |> Query.find (versionSelector version)
  1060                              |> Query.find pinButtonSelector
  1061                              |> Event.simulate Event.click
  1062                              |> Event.toResult
  1063                              |> Expect.err
  1064                  , test "all pin buttons have dark background" <|
  1065                      \_ ->
  1066                          init
  1067                              |> givenResourceIsNotPinned
  1068                              |> givenVersionsWithoutPagination
  1069                              |> queryView
  1070                              |> Query.find [ class "resource-versions" ]
  1071                              |> Query.findAll anyVersionSelector
  1072                              |> Query.each
  1073                                  (Query.find pinButtonSelector
  1074                                      >> Query.has [ style "background-color" "#1e1d1d" ]
  1075                                  )
  1076                  ]
  1077              ]
  1078          , describe "given resource is pinned dynamically"
  1079              [ describe "pin bar" <|
  1080                  let
  1081                      pinIcon =
  1082                          init
  1083                              |> givenResourcePinnedDynamically
  1084                              |> queryView
  1085                              |> Query.find [ id "pin-icon" ]
  1086  
  1087                      pinIconArchived =
  1088                          init
  1089                              |> givenResourcePinnedDynamically
  1090                              |> givenThePipelineIsArchived
  1091                              |> queryView
  1092                              |> Query.find [ id "pin-icon" ]
  1093  
  1094                      pinBar =
  1095                          init
  1096                              |> givenResourcePinnedDynamically
  1097                              |> queryView
  1098                              |> Query.find [ id "pin-bar" ]
  1099                  in
  1100                  [ test "when mousing over pin bar, tooltip does not appear" <|
  1101                      \_ ->
  1102                          pinBar
  1103                              |> Event.simulate Event.mouseEnter
  1104                              |> Event.toResult
  1105                              |> Expect.err
  1106                  , test "aligns its children on top left" <|
  1107                      \_ ->
  1108                          pinBar
  1109                              |> Query.has [ style "align-items" "flex-start" ]
  1110                  , test "has a white pin icon of size 14 px" <|
  1111                      \_ ->
  1112                          pinBar
  1113                              |> Query.has
  1114                                  (iconSelector
  1115                                      { size = "14px"
  1116                                      , image = Assets.PinIconWhite
  1117                                      }
  1118                                  )
  1119                  , test "pin icon on pin bar has a margin" <|
  1120                      \_ ->
  1121                          pinIcon
  1122                              |> Query.has [ style "margin" "4px 5px 5px 5px" ]
  1123                  , test "pin icon on pin bar has a padding" <|
  1124                      \_ ->
  1125                          pinIcon
  1126                              |> Query.has [ style "padding" "6px" ]
  1127                  , test "pin icon on pin bar has background that fills size" <|
  1128                      \_ ->
  1129                          pinIcon
  1130                              |> Query.has
  1131                                  [ style "background-size" "contain"
  1132                                  , style "background-origin" "content-box"
  1133                                  ]
  1134                  , test "pin icon on pin bar has a minimum size" <|
  1135                      \_ ->
  1136                          pinIcon
  1137                              |> Query.has
  1138                                  [ style "min-width" "14px"
  1139                                  , style "min-height" "14px"
  1140                                  ]
  1141                  , test "pin icon on pin bar has pointer cursor" <|
  1142                      \_ ->
  1143                          pinIcon
  1144                              |> Query.has pointerCursor
  1145                  , test "clicking pin icon on bar triggers UnpinVersion msg" <|
  1146                      \_ ->
  1147                          pinIcon
  1148                              |> Event.simulate Event.click
  1149                              |> Event.expect
  1150                                  (Msgs.Update <|
  1151                                      Message.Message.Click Message.Message.PinIcon
  1152                                  )
  1153                  , test "mousing over pin icon triggers PinIconHover msg" <|
  1154                      \_ ->
  1155                          pinIcon
  1156                              |> Event.simulate Event.mouseEnter
  1157                              |> Event.expect
  1158                                  (Msgs.Update <|
  1159                                      Message.Message.Hover <|
  1160                                          Just Message.Message.PinIcon
  1161                                  )
  1162                  , test "mousing over pin icon for archived pipeline does nothing" <|
  1163                      \_ ->
  1164                          pinIconArchived
  1165                              |> Event.simulate Event.mouseEnter
  1166                              |> Event.toResult
  1167                              |> Expect.err
  1168                  , test "clicking pin icon for archived pipeline does nothing" <|
  1169                      \_ ->
  1170                          pinIconArchived
  1171                              |> Event.simulate Event.click
  1172                              |> Event.toResult
  1173                              |> Expect.err
  1174                  , test "pin icon for archived pipeline has regular cursor" <|
  1175                      \_ ->
  1176                          pinIconArchived
  1177                              |> Query.has [ style "cursor" "default" ]
  1178                  , test "TogglePinIconHover msg causes pin icon to have dark background" <|
  1179                      \_ ->
  1180                          init
  1181                              |> givenResourcePinnedDynamically
  1182                              |> update
  1183                                  (Message.Message.Hover <|
  1184                                      Just Message.Message.PinIcon
  1185                                  )
  1186                              |> Tuple.first
  1187                              |> queryView
  1188                              |> Query.find [ id "pin-icon" ]
  1189                              |> Query.has [ style "background-color" darkGreyHex ]
  1190                  , test "mousing off pin icon triggers Hover Nothing msg" <|
  1191                      \_ ->
  1192                          init
  1193                              |> givenResourcePinnedDynamically
  1194                              |> update
  1195                                  (Message.Message.Hover <|
  1196                                      Just Message.Message.PinIcon
  1197                                  )
  1198                              |> Tuple.first
  1199                              |> queryView
  1200                              |> Query.find [ id "pin-icon" ]
  1201                              |> Event.simulate Event.mouseLeave
  1202                              |> Event.expect
  1203                                  (Msgs.Update <| Message.Message.Hover <| Nothing)
  1204                  , test "second TogglePinIconHover msg causes pin icon to have transparent background color" <|
  1205                      \_ ->
  1206                          init
  1207                              |> givenResourcePinnedDynamically
  1208                              |> update
  1209                                  (Message.Message.Hover <|
  1210                                      Just Message.Message.PinIcon
  1211                                  )
  1212                              |> Tuple.first
  1213                              |> update (Message.Message.Hover Nothing)
  1214                              |> Tuple.first
  1215                              |> queryView
  1216                              |> Query.find [ id "pin-icon" ]
  1217                              |> Query.has [ style "background-color" "transparent" ]
  1218                  , test "has a table of versions with top, right, and bottom margin" <|
  1219                      \_ ->
  1220                          pinBar
  1221                              |> Query.find [ tag "table" ]
  1222                              |> Query.has [ style "margin" "8px 8px 8px 0" ]
  1223                  , test "pin bar is not visible when upon successful VersionUnpinned msg" <|
  1224                      \_ ->
  1225                          init
  1226                              |> givenResourcePinnedDynamically
  1227                              |> givenVersionsWithoutPagination
  1228                              |> clickToUnpin
  1229                              |> Application.handleCallback (Callback.VersionUnpinned (Ok ()))
  1230                              |> Tuple.first
  1231                              |> queryView
  1232                              |> Query.findAll [ id "pin-bar" ]
  1233                              |> Query.count (Expect.equal 0)
  1234                  , test "pin bar shows unpinned state upon receiving failing (VersionUnpinned) msg" <|
  1235                      \_ ->
  1236                          init
  1237                              |> givenResourcePinnedDynamically
  1238                              |> givenVersionsWithoutPagination
  1239                              |> clickToUnpin
  1240                              |> Application.handleCallback
  1241                                  (Callback.VersionUnpinned Data.httpInternalServerError)
  1242                              |> Tuple.first
  1243                              |> queryView
  1244                              |> pinBarHasPinnedState version
  1245                  , test "pin icon on pin bar is white" <|
  1246                      \_ ->
  1247                          pinIcon
  1248                              |> Query.has
  1249                                  [ style "background-image" <|
  1250                                      Assets.backgroundImage <|
  1251                                          Just Assets.PinIconWhite
  1252                                  ]
  1253                  ]
  1254              , describe "versions list"
  1255                  [ test "version pin states reflect resource pin state" <|
  1256                      \_ ->
  1257                          Resource.init
  1258                              { resourceId = Data.resourceId
  1259                              , paging = Nothing
  1260                              }
  1261                              |> Resource.handleCallback
  1262                                  (Callback.ResourceFetched <|
  1263                                      Ok
  1264                                          { teamName = teamName
  1265                                          , pipelineName = pipelineName
  1266                                          , name = resourceName
  1267                                          , lastChecked = Nothing
  1268                                          , pinnedVersion = Just (Dict.fromList [ ( "version", version ) ])
  1269                                          , pinnedInConfig = False
  1270                                          , pinComment = Nothing
  1271                                          , icon = Nothing
  1272                                          , build = Nothing
  1273                                          }
  1274                                  )
  1275                                  session
  1276                              |> Resource.handleCallback
  1277                                  (Callback.VersionedResourcesFetched <|
  1278                                      Ok
  1279                                          ( Resource.startingPage
  1280                                          , { content =
  1281                                                  [ { id = versionID.versionID
  1282                                                    , version = Dict.fromList [ ( "version", version ) ]
  1283                                                    , metadata = []
  1284                                                    , enabled = True
  1285                                                    }
  1286                                                  , { id = otherVersionID.versionID
  1287                                                    , version = Dict.fromList [ ( "version", otherVersion ) ]
  1288                                                    , metadata = []
  1289                                                    , enabled = True
  1290                                                    }
  1291                                                  , { id = disabledVersionID.versionID
  1292                                                    , version = Dict.fromList [ ( "version", disabledVersion ) ]
  1293                                                    , metadata = []
  1294                                                    , enabled = False
  1295                                                    }
  1296                                                  ]
  1297                                            , pagination = emptyPagination
  1298                                            }
  1299                                          )
  1300                                  )
  1301                                  session
  1302                              |> Tuple.first
  1303                              |> Resource.versions
  1304                              |> List.map .pinState
  1305                              |> Expect.equal
  1306                                  [ PinnedDynamically
  1307                                  , NotThePinnedVersion
  1308                                  , NotThePinnedVersion
  1309                                  ]
  1310                  , test "switching pins puts both versions in transition states" <|
  1311                      \_ ->
  1312                          Resource.init
  1313                              { resourceId = Data.resourceId
  1314                              , paging = Nothing
  1315                              }
  1316                              |> Resource.handleCallback
  1317                                  (Callback.ResourceFetched <|
  1318                                      Ok
  1319                                          { teamName = teamName
  1320                                          , pipelineName = pipelineName
  1321                                          , name = resourceName
  1322                                          , lastChecked = Nothing
  1323                                          , pinnedVersion = Just (Dict.fromList [ ( "version", version ) ])
  1324                                          , pinnedInConfig = False
  1325                                          , pinComment = Nothing
  1326                                          , icon = Nothing
  1327                                          , build = Nothing
  1328                                          }
  1329                                  )
  1330                                  session
  1331                              |> Resource.handleCallback
  1332                                  (Callback.VersionedResourcesFetched <|
  1333                                      Ok
  1334                                          ( Resource.startingPage
  1335                                          , { content =
  1336                                                  [ { id = versionID.versionID
  1337                                                    , version = Dict.fromList [ ( "version", version ) ]
  1338                                                    , metadata = []
  1339                                                    , enabled = True
  1340                                                    }
  1341                                                  , { id = otherVersionID.versionID
  1342                                                    , version = Dict.fromList [ ( "version", otherVersion ) ]
  1343                                                    , metadata = []
  1344                                                    , enabled = True
  1345                                                    }
  1346                                                  , { id = disabledVersionID.versionID
  1347                                                    , version = Dict.fromList [ ( "version", disabledVersion ) ]
  1348                                                    , metadata = []
  1349                                                    , enabled = False
  1350                                                    }
  1351                                                  ]
  1352                                            , pagination = emptyPagination
  1353                                            }
  1354                                          )
  1355                                  )
  1356                                  session
  1357                              |> Resource.update (Click <| PinButton otherVersionID)
  1358                              |> Tuple.first
  1359                              |> Resource.versions
  1360                              |> List.map .pinState
  1361                              |> Expect.equal
  1362                                  [ InTransition
  1363                                  , InTransition
  1364                                  , Disabled
  1365                                  ]
  1366                  , test "successful PinResource call when switching shows new version pinned" <|
  1367                      \_ ->
  1368                          Resource.init
  1369                              { resourceId = Data.resourceId
  1370                              , paging = Nothing
  1371                              }
  1372                              |> Resource.handleDelivery session (ClockTicked OneSecond (Time.millisToPosix 1000))
  1373                              |> Resource.handleCallback
  1374                                  (Callback.ResourceFetched <|
  1375                                      Ok
  1376                                          { teamName = teamName
  1377                                          , pipelineName = pipelineName
  1378                                          , name = resourceName
  1379                                          , lastChecked = Nothing
  1380                                          , pinnedVersion = Just (Dict.fromList [ ( "version", version ) ])
  1381                                          , pinnedInConfig = False
  1382                                          , pinComment = Nothing
  1383                                          , icon = Nothing
  1384                                          , build = Nothing
  1385                                          }
  1386                                  )
  1387                                  session
  1388                              |> Resource.handleCallback
  1389                                  (Callback.VersionedResourcesFetched <|
  1390                                      Ok
  1391                                          ( Resource.startingPage
  1392                                          , { content =
  1393                                                  [ { id = versionID.versionID
  1394                                                    , version = Dict.fromList [ ( "version", version ) ]
  1395                                                    , metadata = []
  1396                                                    , enabled = True
  1397                                                    }
  1398                                                  , { id = otherVersionID.versionID
  1399                                                    , version = Dict.fromList [ ( "version", otherVersion ) ]
  1400                                                    , metadata = []
  1401                                                    , enabled = True
  1402                                                    }
  1403                                                  , { id = disabledVersionID.versionID
  1404                                                    , version = Dict.fromList [ ( "version", disabledVersion ) ]
  1405                                                    , metadata = []
  1406                                                    , enabled = False
  1407                                                    }
  1408                                                  ]
  1409                                            , pagination = emptyPagination
  1410                                            }
  1411                                          )
  1412                                  )
  1413                                  session
  1414                              |> Resource.update (Click <| PinButton otherVersionID)
  1415                              |> Resource.handleCallback
  1416                                  (Callback.VersionPinned <| Ok ())
  1417                                  session
  1418                              |> Tuple.first
  1419                              |> Resource.versions
  1420                              |> List.map .pinState
  1421                              |> Expect.equal
  1422                                  [ NotThePinnedVersion
  1423                                  , PinnedDynamically
  1424                                  , NotThePinnedVersion
  1425                                  ]
  1426                  , test "auto-refresh respects pin switching" <|
  1427                      \_ ->
  1428                          Resource.init
  1429                              { resourceId = Data.resourceId
  1430                              , paging = Nothing
  1431                              }
  1432                              |> Resource.handleDelivery session (ClockTicked OneSecond (Time.millisToPosix 1000))
  1433                              |> Resource.handleCallback
  1434                                  (Callback.ResourceFetched <|
  1435                                      Ok
  1436                                          { teamName = teamName
  1437                                          , pipelineName = pipelineName
  1438                                          , name = resourceName
  1439                                          , lastChecked = Nothing
  1440                                          , pinnedVersion = Just (Dict.fromList [ ( "version", version ) ])
  1441                                          , pinnedInConfig = False
  1442                                          , pinComment = Nothing
  1443                                          , icon = Nothing
  1444                                          , build = Nothing
  1445                                          }
  1446                                  )
  1447                                  session
  1448                              |> Resource.handleCallback
  1449                                  (Callback.VersionedResourcesFetched <|
  1450                                      Ok
  1451                                          ( Resource.startingPage
  1452                                          , { content =
  1453                                                  [ { id = versionID.versionID
  1454                                                    , version = Dict.fromList [ ( "version", version ) ]
  1455                                                    , metadata = []
  1456                                                    , enabled = True
  1457                                                    }
  1458                                                  , { id = otherVersionID.versionID
  1459                                                    , version = Dict.fromList [ ( "version", otherVersion ) ]
  1460                                                    , metadata = []
  1461                                                    , enabled = True
  1462                                                    }
  1463                                                  , { id = disabledVersionID.versionID
  1464                                                    , version = Dict.fromList [ ( "version", disabledVersion ) ]
  1465                                                    , metadata = []
  1466                                                    , enabled = False
  1467                                                    }
  1468                                                  ]
  1469                                            , pagination = emptyPagination
  1470                                            }
  1471                                          )
  1472                                  )
  1473                                  session
  1474                              |> Resource.update (Click <| PinButton otherVersionID)
  1475                              |> Resource.handleCallback
  1476                                  (Callback.ResourceFetched <|
  1477                                      Ok
  1478                                          { teamName = teamName
  1479                                          , pipelineName = pipelineName
  1480                                          , name = resourceName
  1481                                          , lastChecked = Nothing
  1482                                          , pinnedVersion = Just (Dict.fromList [ ( "version", version ) ])
  1483                                          , pinnedInConfig = False
  1484                                          , pinComment = Nothing
  1485                                          , icon = Nothing
  1486                                          , build = Nothing
  1487                                          }
  1488                                  )
  1489                                  session
  1490                              |> Tuple.first
  1491                              |> Resource.versions
  1492                              |> List.map .pinState
  1493                              |> Expect.equal
  1494                                  [ InTransition
  1495                                  , InTransition
  1496                                  , Disabled
  1497                                  ]
  1498                  , test "checkbox on pinned version has a purple outline" <|
  1499                      \_ ->
  1500                          init
  1501                              |> givenResourcePinnedDynamically
  1502                              |> givenVersionsWithoutPagination
  1503                              |> queryView
  1504                              |> Query.find (versionSelector version)
  1505                              |> Query.find checkboxSelector
  1506                              |> Query.has purpleOutlineSelector
  1507                  , test "pin button on pinned version has a pointer cursor" <|
  1508                      \_ ->
  1509                          init
  1510                              |> givenResourcePinnedDynamically
  1511                              |> givenVersionsWithoutPagination
  1512                              |> queryView
  1513                              |> Query.find (versionSelector version)
  1514                              |> Query.find pinButtonSelector
  1515                              |> Query.has pointerCursor
  1516                  , test "pin button on an unpinned version has a pointer cursor" <|
  1517                      \_ ->
  1518                          init
  1519                              |> givenResourcePinnedDynamically
  1520                              |> givenVersionsWithoutPagination
  1521                              |> queryView
  1522                              |> Query.find (versionSelector otherVersion)
  1523                              |> Query.find pinButtonSelector
  1524                              |> Query.has pointerCursor
  1525                  , test "pin button on pinned version has click handler" <|
  1526                      \_ ->
  1527                          init
  1528                              |> givenResourcePinnedDynamically
  1529                              |> givenVersionsWithoutPagination
  1530                              |> queryView
  1531                              |> Query.find (versionSelector version)
  1532                              |> Query.find pinButtonSelector
  1533                              |> Event.simulate Event.click
  1534                              |> Event.expect
  1535                                  (Msgs.Update <|
  1536                                      Message.Message.Click <|
  1537                                          Message.Message.PinButton versionID
  1538                                  )
  1539                  , test "pin button on pinned version shows transition state when (UnpinVersion) is received" <|
  1540                      \_ ->
  1541                          init
  1542                              |> givenResourcePinnedDynamically
  1543                              |> givenVersionsWithoutPagination
  1544                              |> clickToUnpin
  1545                              |> queryView
  1546                              |> Query.find (versionSelector version)
  1547                              |> Query.find pinButtonSelector
  1548                              |> pinButtonHasTransitionState
  1549                  , test "pin button on 'v1' still shows transition state on autorefresh before VersionUnpinned is recieved" <|
  1550                      \_ ->
  1551                          init
  1552                              |> givenResourcePinnedDynamically
  1553                              |> givenVersionsWithoutPagination
  1554                              |> clickToUnpin
  1555                              |> givenResourcePinnedDynamically
  1556                              |> queryView
  1557                              |> Query.find (versionSelector version)
  1558                              |> Query.find pinButtonSelector
  1559                              |> pinButtonHasTransitionState
  1560                  , test "version header on pinned version has a purple outline" <|
  1561                      \_ ->
  1562                          init
  1563                              |> givenResourcePinnedDynamically
  1564                              |> givenVersionsWithoutPagination
  1565                              |> queryView
  1566                              |> Query.find (versionSelector version)
  1567                              |> findLast [ tag "div", containing [ text version ] ]
  1568                              |> Query.has purpleOutlineSelector
  1569                  , test "pin button on pinned version has a white icon" <|
  1570                      \_ ->
  1571                          init
  1572                              |> givenResourcePinnedDynamically
  1573                              |> givenVersionsWithoutPagination
  1574                              |> queryView
  1575                              |> Query.find (versionSelector version)
  1576                              |> Query.find pinButtonSelector
  1577                              |> Query.has
  1578                                  [ style "background-image" <|
  1579                                      Assets.backgroundImage <|
  1580                                          Just Assets.PinIconWhite
  1581                                  ]
  1582                  , test "does not show tooltip on the pin button on ToggleVersionTooltip" <|
  1583                      \_ ->
  1584                          init
  1585                              |> givenResourcePinnedDynamically
  1586                              |> givenVersionsWithoutPagination
  1587                              |> hoverOverPinButton
  1588                              |> queryView
  1589                              |> Query.find (versionSelector version)
  1590                              |> Query.hasNot versionTooltipSelector
  1591                  , test "unpinned versions are lower opacity" <|
  1592                      \_ ->
  1593                          init
  1594                              |> givenResourcePinnedDynamically
  1595                              |> givenVersionsWithoutPagination
  1596                              |> queryView
  1597                              |> Query.find (versionSelector otherVersion)
  1598                              |> Query.has [ style "opacity" "0.5" ]
  1599                  , test "all pin buttons have dark background" <|
  1600                      \_ ->
  1601                          init
  1602                              |> givenResourceIsNotPinned
  1603                              |> givenVersionsWithoutPagination
  1604                              |> queryView
  1605                              |> Query.find [ class "resource-versions" ]
  1606                              |> Query.findAll anyVersionSelector
  1607                              |> Query.each
  1608                                  (Query.find pinButtonSelector
  1609                                      >> Query.has [ style "background-color" "#1e1d1d" ]
  1610                                  )
  1611                  , describe "when the pipeline is archived" <|
  1612                      let
  1613                          setup =
  1614                              init
  1615                                  |> givenResourceIsNotPinned
  1616                                  |> givenVersionsWithoutPagination
  1617                                  |> givenThePipelineIsArchived
  1618                                  |> queryView
  1619                      in
  1620                      [ test "has no pin button" <|
  1621                          \_ ->
  1622                              setup |> Query.hasNot pinButtonSelector
  1623                      , test "has no enable checkbox" <|
  1624                          \_ ->
  1625                              setup |> Query.hasNot checkboxSelector
  1626                      ]
  1627                  ]
  1628              , test "resource refreshes on successful VersionUnpinned msg" <|
  1629                  \_ ->
  1630                      init
  1631                          |> givenResourcePinnedDynamically
  1632                          |> givenVersionsWithoutPagination
  1633                          |> clickToUnpin
  1634                          |> Application.handleCallback (Callback.VersionUnpinned (Ok ()))
  1635                          |> Tuple.second
  1636                          |> Expect.equal [ Effects.FetchResource Data.resourceId ]
  1637              , describe "pin comment bar" <|
  1638                  let
  1639                      pinCommentBar =
  1640                          init
  1641                              |> givenResourcePinnedWithComment
  1642                              |> commentBar
  1643                  in
  1644                  [ test "has a grey background" <|
  1645                      \_ ->
  1646                          pinCommentBar
  1647                              |> Query.has
  1648                                  [ style "background-color" "#2e2c2c" ]
  1649                  , test "has a minimal height of 25 px" <|
  1650                      \_ ->
  1651                          pinCommentBar
  1652                              |> Query.has
  1653                                  [ style "min-height" "25px" ]
  1654                  , test "takes 50% width of pin tools" <|
  1655                      \_ ->
  1656                          pinCommentBar
  1657                              |> Query.has
  1658                                  [ style "flex" "1" ]
  1659                  , describe "has an icon container" <|
  1660                      [ test "lays out children horizontally" <|
  1661                          \_ ->
  1662                              init
  1663                                  |> givenResourcePinnedWithComment
  1664                                  |> iconContainer
  1665                                  |> Query.has [ style "display" "flex", style "flex-grow" "1" ]
  1666                      , test "icons align on top of the icon container" <|
  1667                          \_ ->
  1668                              init
  1669                                  |> givenResourcePinnedWithComment
  1670                                  |> iconContainer
  1671                                  |> Query.has [ style "align-items" "flex-start" ]
  1672                      , test "icon div has message icon" <|
  1673                          let
  1674                              iconDiv : Application.Model -> Query.Single Msgs.TopLevelMessage
  1675                              iconDiv =
  1676                                  iconContainer >> Query.children [] >> Query.first
  1677                          in
  1678                          \_ ->
  1679                              init
  1680                                  |> givenResourcePinnedWithComment
  1681                                  |> iconDiv
  1682                                  |> Query.has
  1683                                      [ style "background-image" <|
  1684                                          Assets.backgroundImage <|
  1685                                              Just Assets.MessageIcon
  1686                                      , style "background-size" "contain"
  1687                                      , style "background-position" "50% 50%"
  1688                                      , style "background-repeat" "no-repeat"
  1689                                      , style "background-origin" "content-box"
  1690                                      , style "width" "16px"
  1691                                      , style "height" "16px"
  1692                                      , style "margin" "10px"
  1693                                      ]
  1694                      , describe "comment pre" <|
  1695                          [ test "displays inline and grows to fill available space" <|
  1696                              \_ ->
  1697                                  commentPre |> Query.has [ style "flex-grow" "1", style "margin" "0" ]
  1698                          , test "pre contains the comment" <|
  1699                              \_ ->
  1700                                  commentPre |> Query.has [ text "some pin comment" ]
  1701                          , test "has no default outline" <|
  1702                              \_ ->
  1703                                  commentPre |> Query.has [ style "outline" "0" ]
  1704                          , test "has vertical padding" <|
  1705                              \_ ->
  1706                                  commentPre |> Query.has [ style "padding" "8px 0" ]
  1707                          , test "has a maximum height of 150px" <|
  1708                              \_ ->
  1709                                  commentPre |> Query.has [ style "max-height" "150px" ]
  1710                          , test "can scroll vertically" <|
  1711                              \_ ->
  1712                                  commentPre |> Query.has [ style "overflow-y" "scroll" ]
  1713                          ]
  1714                      , describe "edit button" <|
  1715                          let
  1716                              editButton =
  1717                                  init
  1718                                      |> givenUserIsAuthorized
  1719                                      |> givenResourcePinnedWithComment
  1720                                      |> iconContainer
  1721                                      |> Query.find [ id "edit-button" ]
  1722                          in
  1723                          [ test "only shows up when user is authorized" <|
  1724                              \_ ->
  1725                                  init
  1726                                      |> givenUserIsAuthorized
  1727                                      |> givenResourcePinnedWithComment
  1728                                      |> commentBar
  1729                                      |> Query.has [ id "edit-button" ]
  1730                          , test "edit button is on the far right" <|
  1731                              \_ ->
  1732                                  init
  1733                                      |> givenUserIsAuthorized
  1734                                      |> givenResourcePinnedWithComment
  1735                                      |> iconContainer
  1736                                      |> Query.children []
  1737                                      |> Query.index -1
  1738                                      |> Query.has [ id "edit-button" ]
  1739                          , test "has the pencil icon" <|
  1740                              \_ ->
  1741                                  editButton
  1742                                      |> Query.has (iconSelector { size = "16px", image = Assets.PencilIcon })
  1743                          , test "has padding of 5 px" <|
  1744                              \_ ->
  1745                                  editButton
  1746                                      |> Query.has [ style "padding" "5px" ]
  1747                          , test "has margin of 5 px" <|
  1748                              \_ ->
  1749                                  editButton
  1750                                      |> Query.has [ style "margin" "5px" ]
  1751                          , test "has background that fills size" <|
  1752                              \_ ->
  1753                                  editButton
  1754                                      |> Query.has
  1755                                          [ style "background-size" "contain"
  1756                                          , style "background-origin" "content-box"
  1757                                          ]
  1758                          , test "has a pointer cursor" <|
  1759                              \_ ->
  1760                                  editButton
  1761                                      |> Query.has [ style "cursor" "pointer" ]
  1762                          , defineHoverBehaviour
  1763                              { name = "edit button"
  1764                              , setup = init |> givenUserIsAuthorized |> givenResourcePinnedWithComment
  1765                              , query = iconContainer >> Query.find [ id "edit-button" ]
  1766                              , unhoveredSelector =
  1767                                  { description = "transparent background"
  1768                                  , selector = []
  1769                                  }
  1770                              , hoverable = Message.Message.EditButton
  1771                              , hoveredSelector =
  1772                                  { description = "dark background"
  1773                                  , selector =
  1774                                      [ style "background-color" almostBlack ]
  1775                                  }
  1776                              }
  1777                          , test "has a click handler" <|
  1778                              \_ ->
  1779                                  editButton
  1780                                      |> Event.simulate Event.click
  1781                                      |> Event.expect
  1782                                          (Msgs.Update <|
  1783                                              Message.Message.Click <|
  1784                                                  Message.Message.EditButton
  1785                                          )
  1786                          , test "after clicking on edit button, the background turns purple" <|
  1787                              \_ ->
  1788                                  init
  1789                                      |> givenResourcePinnedWithComment
  1790                                      |> update
  1791                                          (Message.Message.Click <|
  1792                                              Message.Message.EditButton
  1793                                          )
  1794                                      |> Tuple.first
  1795                                      |> iconContainer
  1796                                      |> Query.has [ style "background-color" purpleHex ]
  1797                          , test "after clicking on edit button, edit button disappears" <|
  1798                              \_ ->
  1799                                  init
  1800                                      |> givenUserIsAuthorized
  1801                                      |> givenResourcePinnedWithComment
  1802                                      |> update
  1803                                          (Message.Message.Click <|
  1804                                              Message.Message.EditButton
  1805                                          )
  1806                                      |> Tuple.first
  1807                                      |> iconContainer
  1808                                      |> Query.hasNot [ id "edit-button" ]
  1809                          , test "after clicking on edit button, the textarea is focused" <|
  1810                              \_ ->
  1811                                  init
  1812                                      |> givenResourcePinnedWithComment
  1813                                      |> update
  1814                                          (Message.Message.Click <|
  1815                                              Message.Message.EditButton
  1816                                          )
  1817                                      |> Tuple.second
  1818                                      |> Common.contains (Effects.Focus <| Effects.toHtmlID ResourceCommentTextarea)
  1819                          , test "after clicking on edit button, there's a save button" <|
  1820                              \_ ->
  1821                                  init
  1822                                      |> givenUserIsAuthorized
  1823                                      |> givenResourcePinnedWithComment
  1824                                      |> update
  1825                                          (Message.Message.Click <|
  1826                                              Message.Message.EditButton
  1827                                          )
  1828                                      |> Tuple.first
  1829                                      |> iconContainer
  1830                                      |> Query.has [ tag "button" ]
  1831                          ]
  1832                      , describe "textarea" <|
  1833                          let
  1834                              textarea =
  1835                                  init
  1836                                      |> givenUserIsAuthorized
  1837                                      |> givenResourcePinnedWithComment
  1838                                      |> commentBar
  1839                                      |> Query.find [ tag "textarea" ]
  1840                          in
  1841                          [ test "only shows up when user is authorized and editing" <|
  1842                              \_ ->
  1843                                  init
  1844                                      |> givenUserIsAuthorized
  1845                                      |> givenResourcePinnedWithComment
  1846                                      |> commentBar
  1847                                      |> Query.has [ tag "textarea" ]
  1848                          , test "has comment as value" <|
  1849                              \_ ->
  1850                                  textarea
  1851                                      |> Query.has
  1852                                          [ attribute <|
  1853                                              Attr.value "some pin comment"
  1854                                          ]
  1855                          , test "has the id resource_comment" <|
  1856                              \_ ->
  1857                                  textarea |> Query.has [ id "resource_comment" ]
  1858                          , test "has a transparent background" <|
  1859                              \_ ->
  1860                                  textarea |> Query.has [ style "background-color" "transparent" ]
  1861                          , test "has no border or outline" <|
  1862                              \_ ->
  1863                                  textarea |> Query.has [ style "outline" "none", style "border" "none" ]
  1864                          , test "has the text color" <|
  1865                              \_ ->
  1866                                  textarea |> Query.has [ style "color" "#e6e7e8" ]
  1867                          , test "has no resize handler" <|
  1868                              \_ ->
  1869                                  textarea |> Query.has [ style "resize" "none" ]
  1870                          , test "has border-box" <|
  1871                              \_ ->
  1872                                  textarea
  1873                                      |> Query.has [ style "box-sizing" "border-box" ]
  1874                          , test "has 1 row by default" <|
  1875                              \_ ->
  1876                                  textarea
  1877                                      |> Query.has [ attribute <| Attr.rows 1 ]
  1878                          , test "matches app font" <|
  1879                              \_ ->
  1880                                  textarea
  1881                                      |> Query.has
  1882                                          [ style "font-size" "12px"
  1883                                          , style "font-family" Views.Styles.fontFamilyDefault
  1884                                          , style "font-weight" Views.Styles.fontWeightDefault
  1885                                          ]
  1886                          , test "has a max height of 150px" <|
  1887                              \_ ->
  1888                                  textarea
  1889                                      |> Query.has [ style "max-height" "150px" ]
  1890                          , test "has flex-grow 1" <|
  1891                              \_ ->
  1892                                  textarea
  1893                                      |> Query.has [ style "flex-grow" "1" ]
  1894                          , test "has a top and bottom margin" <|
  1895                              \_ ->
  1896                                  textarea
  1897                                      |> Query.has [ style "margin" "8px 0" ]
  1898                          , test "is readonly when not editing" <|
  1899                              \_ ->
  1900                                  textarea
  1901                                      |> Query.has [ attribute <| Attr.readonly True ]
  1902                          , describe "sync textarea height" <|
  1903                              [ test "sync when editing" <|
  1904                                  \_ ->
  1905                                      init
  1906                                          |> givenUserIsAuthorized
  1907                                          |> givenResourcePinnedWithComment
  1908                                          |> whenUserEditsComment
  1909                                          |> Tuple.second
  1910                                          |> Common.contains (Effects.SyncTextareaHeight ResourceCommentTextarea)
  1911                              , test "sync when resource is first loaded" <|
  1912                                  \_ ->
  1913                                      init
  1914                                          |> givenUserIsAuthorized
  1915                                          |> whenResourceLoadsWithPinnedComment
  1916                                          |> Tuple.second
  1917                                          |> Common.contains (Effects.SyncTextareaHeight ResourceCommentTextarea)
  1918                              , test "subscribes to window resize" <|
  1919                                  \_ ->
  1920                                      init
  1921                                          |> Application.subscriptions
  1922                                          |> Common.contains Subscription.OnWindowResize
  1923                              , test "sync when window is resized" <|
  1924                                  \_ ->
  1925                                      init
  1926                                          |> givenUserIsAuthorized
  1927                                          |> givenResourcePinnedWithComment
  1928                                          |> Application.handleDelivery
  1929                                              (Subscription.WindowResized 0 0)
  1930                                          |> Tuple.second
  1931                                          |> Common.contains (Effects.SyncTextareaHeight ResourceCommentTextarea)
  1932                              ]
  1933                          , describe "when editing the textarea" <|
  1934                              [ test "is not readonly" <|
  1935                                  \_ ->
  1936                                      init
  1937                                          |> givenUserIsAuthorized
  1938                                          |> givenResourcePinnedWithComment
  1939                                          |> givenUserEditedComment
  1940                                          |> Application.update (Msgs.Update <| Message.Message.Click Message.Message.EditButton)
  1941                                          |> Tuple.first
  1942                                          |> queryView
  1943                                          |> Query.find [ tag "textarea" ]
  1944                                          |> Query.has [ attribute <| Attr.readonly False ]
  1945                              , test "input in textarea produces EditComment msg" <|
  1946                                  \_ ->
  1947                                      textarea
  1948                                          |> Event.simulate (Event.input "foo")
  1949                                          |> Event.expect
  1950                                              (Msgs.Update <|
  1951                                                  Message.Message.EditComment "foo"
  1952                                              )
  1953                              , test "EditComment updates textarea value" <|
  1954                                  \_ ->
  1955                                      init
  1956                                          |> givenUserIsAuthorized
  1957                                          |> givenResourcePinnedWithComment
  1958                                          |> givenUserEditedComment
  1959                                          |> queryView
  1960                                          |> Query.find [ tag "textarea" ]
  1961                                          |> Query.has [ attribute <| Attr.value "foo" ]
  1962                              , test "autorefresh doesn't change textarea" <|
  1963                                  \_ ->
  1964                                      init
  1965                                          |> givenUserIsAuthorized
  1966                                          |> givenResourcePinnedWithComment
  1967                                          |> givenUserEditedComment
  1968                                          |> givenResourcePinnedWithComment
  1969                                          |> queryView
  1970                                          |> Query.find [ tag "textarea" ]
  1971                                          |> Query.has
  1972                                              [ attribute <| Attr.value "foo" ]
  1973                              , test "focusing textarea triggers FocusTextArea msg" <|
  1974                                  \_ ->
  1975                                      textarea
  1976                                          |> Event.simulate Event.focus
  1977                                          |> Event.expect
  1978                                              (Msgs.Update
  1979                                                  Message.Message.FocusTextArea
  1980                                              )
  1981                              , test "keydown subscription active when textarea is focused" <|
  1982                                  \_ ->
  1983                                      init
  1984                                          |> givenUserIsAuthorized
  1985                                          |> givenResourcePinnedWithComment
  1986                                          |> givenTextareaFocused
  1987                                          |> Application.subscriptions
  1988                                          |> Common.contains Subscription.OnKeyDown
  1989                              , test "keyup subscription active when textarea is focused" <|
  1990                                  \_ ->
  1991                                      init
  1992                                          |> givenUserIsAuthorized
  1993                                          |> givenResourcePinnedWithComment
  1994                                          |> givenTextareaFocused
  1995                                          |> Application.subscriptions
  1996                                          |> Common.contains Subscription.OnKeyUp
  1997                              , test "Ctrl-Enter sends SaveComment msg" <|
  1998                                  \_ ->
  1999                                      init
  2000                                          |> givenUserIsAuthorized
  2001                                          |> givenResourcePinnedWithComment
  2002                                          |> givenUserEditedComment
  2003                                          |> givenTextareaFocused
  2004                                          |> pressControlEnter
  2005                                          |> Tuple.second
  2006                                          |> Expect.equal [ Effects.SetPinComment Data.resourceId "foo" ]
  2007                              , test "Command + Enter sends SaveComment msg" <|
  2008                                  \_ ->
  2009                                      init
  2010                                          |> givenUserIsAuthorized
  2011                                          |> givenResourcePinnedWithComment
  2012                                          |> givenUserEditedComment
  2013                                          |> givenTextareaFocused
  2014                                          |> pressMetaEnter
  2015                                          |> Tuple.second
  2016                                          |> Expect.equal [ Effects.SetPinComment Data.resourceId "foo" ]
  2017                              , test "blurring input triggers BlurTextArea msg" <|
  2018                                  \_ ->
  2019                                      textarea
  2020                                          |> Event.simulate Event.blur
  2021                                          |> Event.expect
  2022                                              (Msgs.Update
  2023                                                  Message.Message.BlurTextArea
  2024                                              )
  2025                              , test "Ctrl-Enter after blurring input does nothing" <|
  2026                                  \_ ->
  2027                                      init
  2028                                          |> givenUserIsAuthorized
  2029                                          |> givenResourcePinnedWithComment
  2030                                          |> givenUserEditedComment
  2031                                          |> givenTextareaFocused
  2032                                          |> givenTextareaBlurred
  2033                                          |> pressControlEnter
  2034                                          |> Tuple.second
  2035                                          |> Expect.equal []
  2036                              , test "releasing Ctrl key and pressing enter does nothing" <|
  2037                                  \_ ->
  2038                                      init
  2039                                          |> givenUserIsAuthorized
  2040                                          |> givenResourcePinnedWithComment
  2041                                          |> givenUserEditedComment
  2042                                          |> givenTextareaFocused
  2043                                          |> pressEnterKey
  2044                                          |> Tuple.second
  2045                                          |> Expect.equal []
  2046                              ]
  2047                          ]
  2048                      , describe "edit and save buttons' wrapper div" <|
  2049                          let
  2050                              setup =
  2051                                  init
  2052                                      |> givenUserIsAuthorized
  2053                                      |> givenResourcePinnedWithComment
  2054  
  2055                              editSaveWrapper =
  2056                                  setup
  2057                                      |> commentBar
  2058                                      |> Query.find [ id "edit-save-wrapper" ]
  2059                          in
  2060                          [ test "has a fixed width for both buttons" <|
  2061                              \_ ->
  2062                                  editSaveWrapper
  2063                                      |> Query.has
  2064                                          [ style "width" "60px"
  2065                                          ]
  2066                          , test "buttons are right aligned in the div" <|
  2067                              \_ ->
  2068                                  editSaveWrapper
  2069                                      |> Query.has [ style "display" "flex", style "justify-content" "flex-end" ]
  2070                          , test "does not appear if pipeline is archived" <|
  2071                              \_ ->
  2072                                  setup
  2073                                      |> givenThePipelineIsArchived
  2074                                      |> commentBar
  2075                                      |> Query.hasNot [ id "edit-save-wrapper" ]
  2076                          ]
  2077                      , describe "save button" <|
  2078                          let
  2079                              saveButtonUnedited =
  2080                                  init
  2081                                      |> givenUserIsAuthorized
  2082                                      |> givenResourcePinnedWithComment
  2083                                      |> givenUserClicksEditButton
  2084                                      |> iconContainer
  2085                                      |> Query.find [ id "save-button" ]
  2086  
  2087                              saveButton =
  2088                                  init
  2089                                      |> givenUserIsAuthorized
  2090                                      |> givenResourcePinnedWithComment
  2091                                      |> givenUserClicksEditButton
  2092                                      |> givenUserEditedComment
  2093                                      |> iconContainer
  2094                                      |> Query.find [ id "save-button" ]
  2095                          in
  2096                          [ test "after clicking on edit button, there's a save button" <|
  2097                              \_ ->
  2098                                  init
  2099                                      |> givenUserIsAuthorized
  2100                                      |> givenResourcePinnedWithComment
  2101                                      |> givenUserClicksEditButton
  2102                                      |> iconContainer
  2103                                      |> Query.has [ id "save-button" ]
  2104                          , test "has a pointer cursor" <|
  2105                              \_ ->
  2106                                  saveButton
  2107                                      |> Query.has [ style "cursor" "pointer" ]
  2108                          , test "has a white border and nearly white text when the comment has changed" <|
  2109                              \_ ->
  2110                                  saveButton
  2111                                      |> Query.has
  2112                                          [ style "border" ("1px solid " ++ ColorValues.white)
  2113                                          , style "color" "#e6e7e8"
  2114                                          ]
  2115                          , test "has a grey border and text when the comment has not changed" <|
  2116                              \_ ->
  2117                                  saveButtonUnedited
  2118                                      |> Query.has
  2119                                          [ style "border" "1px solid #979797"
  2120                                          , style "color" "#979797"
  2121                                          ]
  2122                          , test "has a border and text color transition" <|
  2123                              \_ ->
  2124                                  saveButton
  2125                                      |> Query.has [ style "transition" "border 200ms ease, color 200ms ease" ]
  2126                          , test "has a margin of 10 px" <|
  2127                              \_ ->
  2128                                  saveButton
  2129                                      |> Query.has [ style "margin" "5px 5px 7px 7px" ]
  2130                          , defineHoverBehaviour
  2131                              { name = "save button"
  2132                              , setup =
  2133                                  init
  2134                                      |> givenUserIsAuthorized
  2135                                      |> givenResourcePinnedWithComment
  2136                                      |> givenUserClicksEditButton
  2137                                      |> givenUserEditedComment
  2138                              , query = iconContainer >> Query.find [ id "save-button" ]
  2139                              , unhoveredSelector =
  2140                                  { description = "transparent background"
  2141                                  , selector = [ style "background-color" "transparent" ]
  2142                                  }
  2143                              , hoverable = Message.Message.SaveCommentButton
  2144                              , hoveredSelector =
  2145                                  { description = "dark background"
  2146                                  , selector =
  2147                                      [ style "background-color" almostBlack ]
  2148                                  }
  2149                              }
  2150                          , defineHoverBehaviour
  2151                              { name = "save button without changing comment"
  2152                              , setup =
  2153                                  init
  2154                                      |> givenUserIsAuthorized
  2155                                      |> givenResourcePinnedWithComment
  2156                                      |> givenUserClicksEditButton
  2157                              , query = iconContainer >> Query.find [ id "save-button" ]
  2158                              , unhoveredSelector =
  2159                                  { description = "transparent background"
  2160                                  , selector = [ style "background-color" "transparent" ]
  2161                                  }
  2162                              , hoverable = Message.Message.SaveCommentButton
  2163                              , hoveredSelector =
  2164                                  { description = "transparent background"
  2165                                  , selector = [ style "background-color" "transparent" ]
  2166                                  }
  2167                              }
  2168                          , test "button click sends SaveComment msg" <|
  2169                              \_ ->
  2170                                  saveButton
  2171                                      |> Event.simulate Event.click
  2172                                      |> Event.expect
  2173                                          (Msgs.Update <|
  2174                                              Message.Message.Click
  2175                                                  Message.Message.SaveCommentButton
  2176                                          )
  2177                          , test "SaveComment msg makes API call if comment has changed" <|
  2178                              \_ ->
  2179                                  init
  2180                                      |> givenUserIsAuthorized
  2181                                      |> givenResourcePinnedWithComment
  2182                                      |> givenUserEditedComment
  2183                                      |> update
  2184                                          (Message.Message.Click Message.Message.SaveCommentButton)
  2185                                      |> Tuple.second
  2186                                      |> Common.contains (Effects.SetPinComment Data.resourceId "foo")
  2187                          , test "SaveComment msg does not make API call if comment has not changed" <|
  2188                              \_ ->
  2189                                  init
  2190                                      |> givenUserIsAuthorized
  2191                                      |> givenResourcePinnedWithComment
  2192                                      |> update
  2193                                          (Message.Message.Click Message.Message.SaveCommentButton)
  2194                                      |> Tuple.second
  2195                                      |> Expect.equal []
  2196                          , describe "button loading state" <|
  2197                              let
  2198                                  givenCommentSavingInProgress : Application.Model
  2199                                  givenCommentSavingInProgress =
  2200                                      init
  2201                                          |> givenUserIsAuthorized
  2202                                          |> givenResourcePinnedWithComment
  2203                                          |> givenUserClicksEditButton
  2204                                          |> givenUserEditedComment
  2205                                          |> update
  2206                                              (Message.Message.Click Message.Message.SaveCommentButton)
  2207                                          |> Tuple.first
  2208  
  2209                                  viewButton : Application.Model -> Query.Single Msgs.TopLevelMessage
  2210                                  viewButton =
  2211                                      Common.queryView >> Query.find [ id "save-button" ]
  2212                              in
  2213                              [ test "shows spinner" <|
  2214                                  \_ ->
  2215                                      givenCommentSavingInProgress
  2216                                          |> viewButton
  2217                                          |> Query.has
  2218                                              [ style "animation"
  2219                                                  "container-rotate 1568ms linear infinite"
  2220                                              , style "height" "12px"
  2221                                              , style "width" "12px"
  2222                                              ]
  2223                              , test "clears button text" <|
  2224                                  \_ ->
  2225                                      givenCommentSavingInProgress
  2226                                          |> viewButton
  2227                                          |> Query.hasNot [ text "save" ]
  2228                              ]
  2229                          ]
  2230                      , describe "when saving completes" <|
  2231                          [ test "on success, leaves editing mode" <|
  2232                              \_ ->
  2233                                  init
  2234                                      |> givenUserIsAuthorized
  2235                                      |> givenResourcePinnedWithComment
  2236                                      |> givenUserClicksEditButton
  2237                                      |> givenUserEditedComment
  2238                                      |> update
  2239                                          (Message.Message.Click Message.Message.SaveCommentButton)
  2240                                      |> Tuple.first
  2241                                      |> Application.handleCallback (CommentSet <| Ok ())
  2242                                      |> Tuple.first
  2243                                      |> iconContainer
  2244                                      |> Query.hasNot [ id "save-button" ]
  2245                          , test "on error, stays in editing mode" <|
  2246                              \_ ->
  2247                                  init
  2248                                      |> givenUserIsAuthorized
  2249                                      |> givenResourcePinnedWithComment
  2250                                      |> givenUserClicksEditButton
  2251                                      |> givenUserEditedComment
  2252                                      |> update
  2253                                          (Message.Message.Click Message.Message.SaveCommentButton)
  2254                                      |> Tuple.first
  2255                                      |> Application.handleCallback
  2256                                          (CommentSet <| Data.httpInternalServerError)
  2257                                      |> Tuple.first
  2258                                      |> iconContainer
  2259                                      |> Query.has [ id "save-button" ]
  2260                          , test "on success, refetches data" <|
  2261                              \_ ->
  2262                                  init
  2263                                      |> givenUserIsAuthorized
  2264                                      |> givenResourcePinnedWithComment
  2265                                      |> givenUserClicksEditButton
  2266                                      |> givenUserEditedComment
  2267                                      |> update
  2268                                          (Message.Message.Click Message.Message.SaveCommentButton)
  2269                                      |> Tuple.first
  2270                                      |> Application.handleCallback (CommentSet <| Ok ())
  2271                                      |> Tuple.second
  2272                                      |> Common.contains (Effects.FetchResource Data.resourceId)
  2273                          , test "on error, refetches data" <|
  2274                              \_ ->
  2275                                  init
  2276                                      |> givenUserIsAuthorized
  2277                                      |> givenResourcePinnedWithComment
  2278                                      |> givenUserClicksEditButton
  2279                                      |> givenUserEditedComment
  2280                                      |> update
  2281                                          (Message.Message.Click Message.Message.SaveCommentButton)
  2282                                      |> Tuple.first
  2283                                      |> Application.handleCallback
  2284                                          (CommentSet <| Data.httpInternalServerError)
  2285                                      |> Tuple.second
  2286                                      |> Common.contains (Effects.FetchResource Data.resourceId)
  2287                          ]
  2288                      ]
  2289                  ]
  2290              ]
  2291          , describe "given resource is not pinned"
  2292              [ test "pin tool is not visible" <|
  2293                  \_ ->
  2294                      init
  2295                          |> givenResourceIsNotPinned
  2296                          |> queryView
  2297                          |> Query.hasNot [ id "pin-tools" ]
  2298              , test "pin bar is not visible" <|
  2299                  \_ ->
  2300                      init
  2301                          |> givenResourceIsNotPinned
  2302                          |> queryView
  2303                          |> Query.hasNot [ id "pin-bar" ]
  2304              , test "pin comment bar is not visible" <|
  2305                  \_ ->
  2306                      init
  2307                          |> givenResourceIsNotPinned
  2308                          |> queryView
  2309                          |> Query.hasNot [ id "comment-bar" ]
  2310              , test "then nothing has purple border" <|
  2311                  \_ ->
  2312                      init
  2313                          |> givenResourceIsNotPinned
  2314                          |> queryView
  2315                          |> Query.hasNot purpleOutlineSelector
  2316              , describe "version headers" <|
  2317                  let
  2318                      allVersions : () -> Query.Multiple Msgs.TopLevelMessage
  2319                      allVersions _ =
  2320                          init
  2321                              |> givenResourceIsNotPinned
  2322                              |> givenVersionsWithoutPagination
  2323                              |> queryView
  2324                              |> Query.find [ class "resource-versions" ]
  2325                              |> Query.findAll anyVersionSelector
  2326                  in
  2327                  [ test "contain elements that are black with a black border" <|
  2328                      allVersions
  2329                          >> Query.each
  2330                              (Query.children []
  2331                                  >> Query.first
  2332                                  >> Query.children []
  2333                                  >> Query.each
  2334                                      (Query.has
  2335                                          [ style "border" <|
  2336                                              "1px solid "
  2337                                                  ++ almostBlack
  2338                                          , style "background-color"
  2339                                              almostBlack
  2340                                          ]
  2341                                      )
  2342                              )
  2343                  , test "checkboxes are 25px x 25px with icon-type backgrounds" <|
  2344                      allVersions
  2345                          >> Query.each
  2346                              (Query.children []
  2347                                  >> Query.first
  2348                                  >> Query.children []
  2349                                  >> Query.first
  2350                                  >> Query.has
  2351                                      [ style "margin-right" "5px"
  2352                                      , style "width" "25px"
  2353                                      , style "height" "25px"
  2354                                      , style "background-repeat" "no-repeat"
  2355                                      , style "background-position" "50% 50%"
  2356                                      ]
  2357                              )
  2358                  , test "pin buttons are 25px x 25px with icon-type backgrounds" <|
  2359                      allVersions
  2360                          >> Query.each
  2361                              (Query.children []
  2362                                  >> Query.first
  2363                                  >> Query.children []
  2364                                  >> Query.index 1
  2365                                  >> Query.has
  2366                                      [ style "margin-right" "5px"
  2367                                      , style "width" "25px"
  2368                                      , style "height" "25px"
  2369                                      , style "background-repeat" "no-repeat"
  2370                                      , style "background-position" "50% 50%"
  2371                                      ]
  2372                              )
  2373                  , test "pin buttons are positioned to anchor their tooltips" <|
  2374                      allVersions
  2375                          >> Query.each
  2376                              (Query.children []
  2377                                  >> Query.first
  2378                                  >> Query.children []
  2379                                  >> Query.index 1
  2380                                  >> Query.has
  2381                                      [ style "position" "relative" ]
  2382                              )
  2383                  , test "version headers lay out horizontally, centering" <|
  2384                      allVersions
  2385                          >> Query.each
  2386                              (Query.children []
  2387                                  >> Query.first
  2388                                  >> Query.children []
  2389                                  >> Query.index 2
  2390                                  >> Query.has
  2391                                      [ style "display" "flex"
  2392                                      , style "align-items" "center"
  2393                                      ]
  2394                              )
  2395                  , test "version headers fill horizontal space" <|
  2396                      allVersions
  2397                          >> Query.each
  2398                              (Query.children []
  2399                                  >> Query.first
  2400                                  >> Query.children []
  2401                                  >> Query.index 2
  2402                                  >> Query.has
  2403                                      [ style "flex-grow" "1" ]
  2404                              )
  2405                  , test "version headers have pointer cursor" <|
  2406                      allVersions
  2407                          >> Query.each
  2408                              (Query.children []
  2409                                  >> Query.first
  2410                                  >> Query.children []
  2411                                  >> Query.index 2
  2412                                  >> Query.has
  2413                                      [ style "cursor" "pointer" ]
  2414                              )
  2415                  , test "version headers have contents offset from the left" <|
  2416                      allVersions
  2417                          >> Query.each
  2418                              (Query.children []
  2419                                  >> Query.first
  2420                                  >> Query.children []
  2421                                  >> Query.index 2
  2422                                  >> Query.has
  2423                                      [ style "padding-left" "10px" ]
  2424                              )
  2425                  ]
  2426              , test "clicking pin icon on pin bar does nothing" <|
  2427                  \_ ->
  2428                      init
  2429                          |> givenResourceIsNotPinned
  2430                          |> queryView
  2431                          |> Query.find [ id "pin-icon" ]
  2432                          |> Event.simulate Event.click
  2433                          |> Event.toResult
  2434                          |> Expect.err
  2435              , test "mousing over pin icon does nothing" <|
  2436                  \_ ->
  2437                      init
  2438                          |> givenResourceIsNotPinned
  2439                          |> queryView
  2440                          |> Query.find [ id "pin-icon" ]
  2441                          |> Event.simulate Event.mouseEnter
  2442                          |> Event.toResult
  2443                          |> Expect.err
  2444              , test "does not show tooltip on the pin icon on ToggleVersionTooltip" <|
  2445                  \_ ->
  2446                      init
  2447                          |> givenResourceIsNotPinned
  2448                          |> givenVersionsWithoutPagination
  2449                          |> hoverOverPinButton
  2450                          |> queryView
  2451                          |> Query.find (versionSelector version)
  2452                          |> Query.hasNot versionTooltipSelector
  2453              , test "all pin buttons have pointer cursor" <|
  2454                  \_ ->
  2455                      init
  2456                          |> givenResourceIsNotPinned
  2457                          |> givenVersionsWithoutPagination
  2458                          |> queryView
  2459                          |> Query.find [ class "resource-versions" ]
  2460                          |> Query.findAll anyVersionSelector
  2461                          |> Query.each
  2462                              (Query.find pinButtonSelector
  2463                                  >> Query.has pointerCursor
  2464                              )
  2465              , test "all pin buttons have dark background" <|
  2466                  \_ ->
  2467                      init
  2468                          |> givenResourceIsNotPinned
  2469                          |> givenVersionsWithoutPagination
  2470                          |> queryView
  2471                          |> Query.find [ class "resource-versions" ]
  2472                          |> Query.findAll anyVersionSelector
  2473                          |> Query.each
  2474                              (Query.find pinButtonSelector
  2475                                  >> Query.has [ style "background-color" "#1e1d1d" ]
  2476                              )
  2477              , describe "clicking pin buttons" <|
  2478                  let
  2479                      setup _ =
  2480                          init
  2481                              |> Application.handleCallback
  2482                                  (Callback.UserFetched <|
  2483                                      Ok
  2484                                          { id = "some-user"
  2485                                          , userName = "some-user"
  2486                                          , name = "some-user"
  2487                                          , email = "some-user"
  2488                                          , isAdmin = False
  2489                                          , teams =
  2490                                              Dict.fromList
  2491                                                  [ ( "team"
  2492                                                    , [ "member" ]
  2493                                                    )
  2494                                                  ]
  2495                                          }
  2496                                  )
  2497                              |> Tuple.first
  2498                              |> Application.update
  2499                                  (Msgs.DeliveryReceived <|
  2500                                      ClockTicked
  2501                                          OneSecond
  2502                                          (Time.millisToPosix 0)
  2503                                  )
  2504                              |> Tuple.first
  2505                              |> givenResourceIsNotPinned
  2506                              |> givenVersionsWithoutPagination
  2507                  in
  2508                  [ test "pin button on 'v1' has click handler" <|
  2509                      setup
  2510                          >> queryView
  2511                          >> Query.find (versionSelector version)
  2512                          >> Query.find pinButtonSelector
  2513                          >> Event.simulate Event.click
  2514                          >> Event.expect
  2515                              (Msgs.Update <|
  2516                                  Message.Message.Click <|
  2517                                      Message.Message.PinButton versionID
  2518                              )
  2519                  , describe "after clicking 'v1' pin button" <|
  2520                      let
  2521                          afterClick =
  2522                              setup >> clickToPin versionID
  2523                      in
  2524                      [ test "clicked button shows transition state" <|
  2525                          afterClick
  2526                              >> queryView
  2527                              >> Query.find (versionSelector version)
  2528                              >> Query.find pinButtonSelector
  2529                              >> pinButtonHasTransitionState
  2530                      , test "other pin buttons disabled" <|
  2531                          afterClick
  2532                              >> queryView
  2533                              >> Query.find (versionSelector otherVersion)
  2534                              >> Query.find pinButtonSelector
  2535                              >> Event.simulate Event.click
  2536                              >> Event.toResult
  2537                              >> Expect.err
  2538                      , test "pin bar shows unpinned state" <|
  2539                          afterClick
  2540                              >> queryView
  2541                              >> pinToolsHasTransitionState
  2542                      , test "autorefresh respects transition state" <|
  2543                          afterClick
  2544                              >> givenResourceIsNotPinned
  2545                              >> queryView
  2546                              >> Query.find (versionSelector version)
  2547                              >> Query.find pinButtonSelector
  2548                              >> pinButtonHasTransitionState
  2549                      , describe "when pinning succeeds" <|
  2550                          let
  2551                              onSuccess =
  2552                                  afterClick
  2553                                      >> Application.handleCallback
  2554                                          (Callback.VersionPinned <| Ok ())
  2555                          in
  2556                          [ test "pin bar reflects 'v1'" <|
  2557                              onSuccess
  2558                                  >> Tuple.first
  2559                                  >> queryView
  2560                                  >> pinBarHasPinnedState version
  2561                          , test "fills in comment input with default text" <|
  2562                              onSuccess
  2563                                  >> Tuple.first
  2564                                  >> commentBar
  2565                                  >> Query.find [ tag "textarea" ]
  2566                                  >> Query.has
  2567                                      [ attribute <|
  2568                                          Attr.value
  2569                                              "pinned by some-user at Jan 1 1970 12:00:00 AM"
  2570                                      ]
  2571                          , test "automatically tries to set comment" <|
  2572                              onSuccess
  2573                                  >> Tuple.second
  2574                                  >> Expect.equal
  2575                                      [ Effects.SetPinComment Data.resourceId
  2576                                          "pinned by some-user at Jan 1 1970 12:00:00 AM"
  2577                                      ]
  2578                          ]
  2579                      , test "clicked button shows unpinned state when pinning fails" <|
  2580                          afterClick
  2581                              >> Application.handleCallback
  2582                                  (Callback.VersionPinned Data.httpInternalServerError)
  2583                              >> Tuple.first
  2584                              >> queryView
  2585                              >> Query.find (versionSelector version)
  2586                              >> Query.find pinButtonSelector
  2587                              >> pinButtonHasUnpinnedState
  2588                      ]
  2589                  ]
  2590              ]
  2591          , describe "pin tools" <|
  2592              let
  2593                  pinTools =
  2594                      init
  2595                          |> givenResourcePinnedDynamically
  2596                          |> queryView
  2597                          |> Query.find [ id "pin-tools" ]
  2598              in
  2599              [ test "has grey background" <|
  2600                  \_ ->
  2601                      pinTools
  2602                          |> Query.has [ style "background-color" "#2e2c2c" ]
  2603              , test "has a minimal height of 28 px" <|
  2604                  \_ ->
  2605                      pinTools
  2606                          |> Query.has [ style "min-height" "28px" ]
  2607              , test "not display check status bar when resources being pinned" <|
  2608                  \_ ->
  2609                      init
  2610                          |> givenResourcePinnedDynamically
  2611                          |> queryView
  2612                          |> Query.hasNot [ class "resource-check-status" ]
  2613              , test "only appears when the resource is pinned" <|
  2614                  \_ ->
  2615                      init
  2616                          |> givenResourceIsNotPinned
  2617                          |> queryView
  2618                          |> Query.hasNot [ id "pin-tools" ]
  2619              , test "has a bottom margin of 24 px" <|
  2620                  \_ ->
  2621                      pinTools
  2622                          |> Query.has [ style "margin-bottom" "24px" ]
  2623              , test "shows a pinned version" <|
  2624                  \_ ->
  2625                      pinTools
  2626                          |> Query.has [ text version ]
  2627              , test "version text vertically centers" <|
  2628                  \_ ->
  2629                      pinTools
  2630                          |> Query.has [ style "display" "flex", style "align-items" "stretch" ]
  2631              , test "after pinning it has a purple border" <|
  2632                  \_ ->
  2633                      pinTools
  2634                          |> Query.has purpleOutlineSelector
  2635              , test "pin tools size includes its border" <|
  2636                  \_ ->
  2637                      pinTools
  2638                          |> Query.has [ style "box-sizing" "border-box" ]
  2639              , test "contains pin bar on the left" <|
  2640                  \_ ->
  2641                      pinTools
  2642                          |> Query.children []
  2643                          |> Query.index 0
  2644                          |> Query.has [ id "pin-bar" ]
  2645              , test "contains pin comment bar on the right" <|
  2646                  \_ ->
  2647                      pinTools
  2648                          |> Query.children []
  2649                          |> Query.index 1
  2650                          |> Query.has [ id "comment-bar" ]
  2651              , test "pin bar and comment bar each takes 50% width" <|
  2652                  \_ ->
  2653                      pinTools
  2654                          |> Query.has [ style "display" "flex" ]
  2655              ]
  2656          , describe "check status" <|
  2657              let
  2658                  checkBar userState =
  2659                      let
  2660                          callback =
  2661                              case userState of
  2662                                  UserStateLoggedIn user ->
  2663                                      UserFetched (Ok user)
  2664  
  2665                                  UserStateLoggedOut ->
  2666                                      LoggedOut (Ok ())
  2667  
  2668                                  UserStateUnknown ->
  2669                                      EmptyCallback
  2670                      in
  2671                      Application.handleCallback callback
  2672                          >> Tuple.first
  2673                          >> queryView
  2674                          >> Query.find [ class "resource-check-status" ]
  2675                          >> Query.children []
  2676                          >> Query.first
  2677              in
  2678              [ test "lays out horizontally" <|
  2679                  \_ ->
  2680                      init
  2681                          |> givenResourceIsNotPinned
  2682                          |> checkBar UserStateLoggedOut
  2683                          |> Query.has [ style "display" "flex" ]
  2684              , test "has two children: check button and status bar" <|
  2685                  \_ ->
  2686                      init
  2687                          |> givenResourceIsNotPinned
  2688                          |> checkBar UserStateLoggedOut
  2689                          |> Query.children []
  2690                          |> Query.count (Expect.equal 2)
  2691              , test "not displayed when pipeline is archived" <|
  2692                  \_ ->
  2693                      init
  2694                          |> givenResourceIsNotPinned
  2695                          |> givenThePipelineIsArchived
  2696                          |> queryView
  2697                          |> Query.hasNot [ class "resource-check-status" ]
  2698              , test "shows 'not checked yet' when there is no build yet" <|
  2699                  \_ ->
  2700                      init
  2701                          |> givenResourceIsNotPinned
  2702                          |> checkBar UserStateLoggedOut
  2703                          |> Query.has [ containing [ text "not checked yet" ] ]
  2704              , describe "when unauthenticated"
  2705                  [ defineHoverBehaviour
  2706                      { name = "check button"
  2707                      , setup = init |> givenResourceIsNotPinned
  2708                      , query = checkBar UserStateLoggedOut >> Query.children [] >> Query.first
  2709                      , unhoveredSelector =
  2710                          { description = "black button with grey refresh icon"
  2711                          , selector =
  2712                              [ style "height" "30px"
  2713                              , style "width" "30px"
  2714                              , style "background-color" almostBlack
  2715                              , style "margin-right" "5px"
  2716                              , containing <|
  2717                                  iconSelector
  2718                                      { size = "20px"
  2719                                      , image = Assets.RefreshIcon
  2720                                      }
  2721                                      ++ [ style "opacity" "0.5"
  2722                                         , style "margin" "5px"
  2723                                         ]
  2724                              ]
  2725                          }
  2726                      , hoverable = Message.Message.CheckButton False
  2727                      , hoveredSelector =
  2728                          { description = "black button with white refresh icon"
  2729                          , selector =
  2730                              [ style "height" "30px"
  2731                              , style "width" "30px"
  2732                              , style "background-color" almostBlack
  2733                              , style "margin-right" "5px"
  2734                              , style "cursor" "pointer"
  2735                              , containing <|
  2736                                  iconSelector
  2737                                      { size = "20px"
  2738                                      , image = Assets.RefreshIcon
  2739                                      }
  2740                                      ++ [ style "opacity" "1"
  2741                                         , style "margin" "5px"
  2742                                         , style "background-size" "contain"
  2743                                         ]
  2744                              ]
  2745                          }
  2746                      }
  2747                  , test "clicking check button sends Check msg" <|
  2748                      \_ ->
  2749                          init
  2750                              |> givenResourceIsNotPinned
  2751                              |> checkBar UserStateLoggedOut
  2752                              |> Query.children []
  2753                              |> Query.first
  2754                              |> Event.simulate Event.click
  2755                              |> Event.expect
  2756                                  (Msgs.Update <|
  2757                                      Message.Message.Click <|
  2758                                          Message.Message.CheckButton False
  2759                                  )
  2760                  , test "Check msg redirects to login" <|
  2761                      \_ ->
  2762                          init
  2763                              |> givenResourceIsNotPinned
  2764                              |> update
  2765                                  (Message.Message.Click <|
  2766                                      Message.Message.CheckButton False
  2767                                  )
  2768                              |> Tuple.second
  2769                              |> Expect.equal [ Effects.RedirectToLogin ]
  2770                  ]
  2771              , describe "when authorized" <|
  2772                  let
  2773                      sampleUser : Concourse.User
  2774                      sampleUser =
  2775                          { id = "test"
  2776                          , userName = "test"
  2777                          , name = "test"
  2778                          , email = "test"
  2779                          , isAdmin = False
  2780                          , teams = Dict.fromList [ ( teamName, [ "member" ] ) ]
  2781                          }
  2782                  in
  2783                  [ defineHoverBehaviour
  2784                      { name = "check button when authorized"
  2785                      , setup =
  2786                          init
  2787                              |> givenResourceIsNotPinned
  2788                      , query = checkBar (UserStateLoggedIn sampleUser) >> Query.children [] >> Query.first
  2789                      , unhoveredSelector =
  2790                          { description = "black button with grey refresh icon"
  2791                          , selector =
  2792                              [ style "height" "30px"
  2793                              , style "width" "30px"
  2794                              , style "background-color" almostBlack
  2795                              , style "margin-right" "5px"
  2796                              , containing <|
  2797                                  iconSelector
  2798                                      { size = "20px"
  2799                                      , image = Assets.RefreshIcon
  2800                                      }
  2801                                      ++ [ style "opacity" "0.5"
  2802                                         , style "margin" "5px"
  2803                                         ]
  2804                              ]
  2805                          }
  2806                      , hoverable = Message.Message.CheckButton True
  2807                      , hoveredSelector =
  2808                          { description = "black button with white refresh icon"
  2809                          , selector =
  2810                              [ style "height" "30px"
  2811                              , style "width" "30px"
  2812                              , style "background-color" almostBlack
  2813                              , style "margin-right" "5px"
  2814                              , style "cursor" "pointer"
  2815                              , containing <|
  2816                                  iconSelector
  2817                                      { size = "20px"
  2818                                      , image = Assets.RefreshIcon
  2819                                      }
  2820                                      ++ [ style "opacity" "1"
  2821                                         , style "margin" "5px"
  2822                                         , style "background-size" "contain"
  2823                                         ]
  2824                              ]
  2825                          }
  2826                      }
  2827                  , test "clicking check button sends Click msg" <|
  2828                      \_ ->
  2829                          init
  2830                              |> givenResourceIsNotPinned
  2831                              |> checkBar (UserStateLoggedIn sampleUser)
  2832                              |> Query.children []
  2833                              |> Query.first
  2834                              |> Event.simulate Event.click
  2835                              |> Event.expect
  2836                                  (Msgs.Update
  2837                                      (Message.Message.Click <|
  2838                                          Message.Message.CheckButton True
  2839                                      )
  2840                                  )
  2841                  , test "Click msg has CheckResource side effect" <|
  2842                      \_ ->
  2843                          init
  2844                              |> givenResourceIsNotPinned
  2845                              |> givenUserIsAuthorized
  2846                              |> update
  2847                                  (Message.Message.Click <|
  2848                                      Message.Message.CheckButton True
  2849                                  )
  2850                              |> Tuple.second
  2851                              |> Expect.equal [ Effects.DoCheck Data.resourceId ]
  2852                  , describe "while check is pending" <|
  2853                      let
  2854                          givenCheckInProgress : Application.Model -> Application.Model
  2855                          givenCheckInProgress =
  2856                              givenResourceIsNotPinned
  2857                                  >> givenUserIsAuthorized
  2858                                  >> update
  2859                                      (Message.Message.Click <|
  2860                                          Message.Message.CheckButton True
  2861                                      )
  2862                                  >> Tuple.first
  2863                      in
  2864                      [ test "clicking check button does nothing" <|
  2865                          \_ ->
  2866                              init
  2867                                  |> givenCheckInProgress
  2868                                  |> checkBar (UserStateLoggedIn sampleUser)
  2869                                  |> Query.children []
  2870                                  |> Query.first
  2871                                  |> Event.simulate Event.click
  2872                                  |> Event.toResult
  2873                                  |> Expect.err
  2874                      , defineHoverBehaviour
  2875                          { name = "check button"
  2876                          , setup = init |> givenCheckInProgress
  2877                          , query = checkBar (UserStateLoggedIn sampleUser) >> Query.children [] >> Query.first
  2878                          , unhoveredSelector =
  2879                              { description = "black button with white refresh icon"
  2880                              , selector =
  2881                                  [ style "height" "30px"
  2882                                  , style "width" "30px"
  2883                                  , style "background-color" almostBlack
  2884                                  , style "margin-right" "5px"
  2885                                  , style "cursor" "default"
  2886                                  , containing <|
  2887                                      iconSelector
  2888                                          { size = "20px"
  2889                                          , image = Assets.RefreshIcon
  2890                                          }
  2891                                          ++ [ style "opacity" "1"
  2892                                             , style "margin" "5px"
  2893                                             ]
  2894                                  ]
  2895                              }
  2896                          , hoverable = Message.Message.CheckButton True
  2897                          , hoveredSelector =
  2898                              { description = "black button with white refresh icon"
  2899                              , selector =
  2900                                  [ style "height" "30px"
  2901                                  , style "width" "30px"
  2902                                  , style "background-color" almostBlack
  2903                                  , style "margin-right" "5px"
  2904                                  , style "cursor" "default"
  2905                                  , containing <|
  2906                                      iconSelector
  2907                                          { size = "20px"
  2908                                          , image = Assets.RefreshIcon
  2909                                          }
  2910                                          ++ [ style "opacity" "1"
  2911                                             , style "margin" "5px"
  2912                                             ]
  2913                                  ]
  2914                              }
  2915                          }
  2916                      ]
  2917                  , test "when check is created, fetches resource" <|
  2918                      \_ ->
  2919                          init
  2920                              |> givenResourceIsNotPinned
  2921                              |> givenUserIsAuthorized
  2922                              |> update
  2923                                  (Message.Message.Click <|
  2924                                      Message.Message.CheckButton True
  2925                                  )
  2926                              |> Tuple.first
  2927                              |> Application.handleCallback
  2928                                  (Callback.Checked <|
  2929                                      Ok <|
  2930                                          Data.build Concourse.BuildStatus.BuildStatusStarted
  2931                                  )
  2932                              |> Tuple.second
  2933                              |> Expect.equal [ Effects.FetchResource Data.resourceId ]
  2934                  , test "when check returns 401, redirects to login" <|
  2935                      \_ ->
  2936                          init
  2937                              |> givenResourceIsNotPinned
  2938                              |> givenUserIsAuthorized
  2939                              |> update
  2940                                  (Message.Message.Click <|
  2941                                      Message.Message.CheckButton True
  2942                                  )
  2943                              |> Tuple.first
  2944                              |> Application.handleCallback
  2945                                  (Callback.Checked <| Data.httpUnauthorized)
  2946                              |> Tuple.second
  2947                              |> Expect.equal [ Effects.RedirectToLogin ]
  2948                  ]
  2949              , describe "when unauthorized" <|
  2950                  let
  2951                      sampleUser : Concourse.User
  2952                      sampleUser =
  2953                          { id = "test"
  2954                          , userName = "test"
  2955                          , name = "test"
  2956                          , email = "test"
  2957                          , isAdmin = False
  2958                          , teams = Dict.fromList [ ( teamName, [ "viewer" ] ) ]
  2959                          }
  2960                  in
  2961                  [ defineHoverBehaviour
  2962                      { name = "check button"
  2963                      , setup =
  2964                          init
  2965                              |> givenResourceIsNotPinned
  2966                      , query = checkBar (UserStateLoggedIn sampleUser) >> Query.children [] >> Query.first
  2967                      , unhoveredSelector =
  2968                          { description = "black button with grey refresh icon"
  2969                          , selector =
  2970                              [ style "height" "30px"
  2971                              , style "width" "30px"
  2972                              , style "background-color" almostBlack
  2973                              , style "margin-right" "5px"
  2974                              , containing <|
  2975                                  iconSelector
  2976                                      { size = "20px"
  2977                                      , image = Assets.RefreshIcon
  2978                                      }
  2979                                      ++ [ style "opacity" "0.5"
  2980                                         , style "margin" "5px"
  2981                                         ]
  2982                              ]
  2983                          }
  2984                      , hoverable = Message.Message.CheckButton False
  2985                      , hoveredSelector =
  2986                          { description = "black button with grey refresh icon"
  2987                          , selector =
  2988                              [ style "height" "30px"
  2989                              , style "width" "30px"
  2990                              , style "background-color" almostBlack
  2991                              , style "margin-right" "5px"
  2992                              , containing <|
  2993                                  iconSelector
  2994                                      { size = "20px"
  2995                                      , image = Assets.RefreshIcon
  2996                                      }
  2997                                      ++ [ style "opacity" "0.5"
  2998                                         , style "margin" "5px"
  2999                                         ]
  3000                              ]
  3001                          }
  3002                      }
  3003                  , test "clicking check button does nothing" <|
  3004                      \_ ->
  3005                          init
  3006                              |> givenResourceIsNotPinned
  3007                              |> checkBar (UserStateLoggedIn sampleUser)
  3008                              |> Query.children []
  3009                              |> Query.first
  3010                              |> Event.simulate Event.click
  3011                              |> Event.toResult
  3012                              |> Expect.err
  3013                  , test "'last checked' time updates with clock ticks" <|
  3014                      \_ ->
  3015                          init
  3016                              |> Application.handleCallback
  3017                                  (Callback.ResourceFetched <|
  3018                                      Ok
  3019                                          { teamName = teamName
  3020                                          , pipelineName = pipelineName
  3021                                          , name = resourceName
  3022                                          , lastChecked = Just (Time.millisToPosix 0)
  3023                                          , pinnedVersion = Nothing
  3024                                          , pinnedInConfig = False
  3025                                          , pinComment = Nothing
  3026                                          , icon = Nothing
  3027                                          , build = Nothing
  3028                                          }
  3029                                  )
  3030                              |> Tuple.first
  3031                              |> Application.update
  3032                                  (Msgs.DeliveryReceived <|
  3033                                      ClockTicked OneSecond <|
  3034                                          Time.millisToPosix (2 * 1000)
  3035                                  )
  3036                              |> Tuple.first
  3037                              |> queryView
  3038                              |> Query.find [ id "last-checked" ]
  3039                              |> Query.has [ text "2s ago" ]
  3040                  , test "'last checked' tooltip respects timezone" <|
  3041                      \_ ->
  3042                          init
  3043                              |> Application.handleCallback
  3044                                  (Callback.ResourceFetched <|
  3045                                      Ok
  3046                                          { teamName = teamName
  3047                                          , pipelineName = pipelineName
  3048                                          , name = resourceName
  3049                                          , lastChecked =
  3050                                              Just
  3051                                                  (Time.millisToPosix 0)
  3052                                          , pinnedVersion = Nothing
  3053                                          , pinnedInConfig = False
  3054                                          , pinComment = Nothing
  3055                                          , icon = Nothing
  3056                                          , build = Nothing
  3057                                          }
  3058                                  )
  3059                              |> Tuple.first
  3060                              |> Application.handleCallback
  3061                                  (Callback.GotCurrentTimeZone <|
  3062                                      Time.customZone (5 * 60) []
  3063                                  )
  3064                              |> Tuple.first
  3065                              |> Application.update
  3066                                  (Msgs.DeliveryReceived <|
  3067                                      ClockTicked OneSecond <|
  3068                                          Time.millisToPosix 1000
  3069                                  )
  3070                              |> Tuple.first
  3071                              |> queryView
  3072                              |> Query.find [ id "last-checked" ]
  3073                              |> Query.has
  3074                                  [ attribute <|
  3075                                      Attr.title "Jan 1 1970 05:00:00 AM"
  3076                                  ]
  3077                  ]
  3078              ]
  3079          , describe "check build" <|
  3080              let
  3081                  baseBuild =
  3082                      Data.build Concourse.BuildStatus.BuildStatusFailed
  3083  
  3084                  resource =
  3085                      { teamName = teamName
  3086                      , pipelineName = pipelineName
  3087                      , name = resourceName
  3088                      , lastChecked = Nothing
  3089                      , pinnedVersion = Nothing
  3090                      , pinnedInConfig = False
  3091                      , pinComment = Nothing
  3092                      , icon = Nothing
  3093                      , build = Just baseBuild
  3094                      }
  3095              in
  3096              [ test "check with build fetches build plan" <|
  3097                  \_ ->
  3098                      init
  3099                          |> Application.handleCallback
  3100                              (Callback.ResourceFetched <| Ok resource)
  3101                          |> Tuple.second
  3102                          |> Common.contains (Effects.FetchBuildPlan 1)
  3103              , test "check with build does not fetch build plan as long as build stays the same" <|
  3104                  \_ ->
  3105                      init
  3106                          |> Application.handleCallback
  3107                              (Callback.ResourceFetched <| Ok resource)
  3108                          |> Tuple.first
  3109                          |> Application.handleCallback
  3110                              (Callback.ResourceFetched <| Ok resource)
  3111                          |> Tuple.second
  3112                          |> Common.notContains (Effects.FetchBuildPlan 1)
  3113              , test "check with build fetches build plan when build changes" <|
  3114                  \_ ->
  3115                      init
  3116                          |> Application.handleCallback
  3117                              (Callback.ResourceFetched <| Ok resource)
  3118                          |> Tuple.first
  3119                          |> Application.handleCallback
  3120                              (Callback.ResourceFetched <|
  3121                                  Ok { resource | build = Just { baseBuild | id = 2 } }
  3122                              )
  3123                          |> Tuple.second
  3124                          |> Common.contains (Effects.FetchBuildPlan 2)
  3125              , describe "build events subscription" <|
  3126                  [ test "after build plan is received, opens event stream" <|
  3127                      \_ ->
  3128                          init
  3129                              |> Application.handleCallback
  3130                                  (Callback.ResourceFetched (Ok resource))
  3131                              |> Tuple.first
  3132                              |> Application.handleCallback
  3133                                  (Callback.PlanAndResourcesFetched 1 <|
  3134                                      Ok <|
  3135                                          ( { id = "plan"
  3136                                            , step = Concourse.BuildStepCheck "step"
  3137                                            }
  3138                                          , { inputs = [], outputs = [] }
  3139                                          )
  3140                                  )
  3141                              |> Expect.all
  3142                                  [ Tuple.second
  3143                                      >> Common.contains
  3144                                          (Effects.OpenBuildEventStream
  3145                                              { url = "/api/v1/builds/1/events"
  3146                                              , eventTypes = [ "end", "event" ]
  3147                                              }
  3148                                          )
  3149                                  , Tuple.first
  3150                                      >> Application.subscriptions
  3151                                      >> Common.contains
  3152                                          (Subscription.FromEventSource
  3153                                              ( "/api/v1/builds/1/events"
  3154                                              , [ "end", "event" ]
  3155                                              )
  3156                                          )
  3157                                  ]
  3158                  , test "if build plan request fails, no event stream is opened" <|
  3159                      \_ ->
  3160                          init
  3161                              |> Application.handleCallback
  3162                                  (Callback.ResourceFetched (Ok resource))
  3163                              |> Tuple.first
  3164                              |> Application.handleCallback
  3165                                  (Callback.PlanAndResourcesFetched 1 <| Data.httpUnauthorized)
  3166                              |> Expect.all
  3167                                  [ Tuple.second >> Expect.equal []
  3168                                  , Tuple.first
  3169                                      >> Application.subscriptions
  3170                                      >> Common.notContains
  3171                                          (Subscription.FromEventSource
  3172                                              ( "/api/v1/builds/1/events"
  3173                                              , [ "end", "event" ]
  3174                                              )
  3175                                          )
  3176                                  ]
  3177                  , describe "build output unavailable" <|
  3178                      let
  3179                          givenBuildWithStatus status err =
  3180                              init
  3181                                  |> Application.handleCallback
  3182                                      (Callback.ResourceFetched (Ok { resource | build = Just (Data.build status) }))
  3183                                  |> Tuple.first
  3184                                  |> Application.handleCallback
  3185                                      (Callback.PlanAndResourcesFetched 1 err)
  3186                                  |> Tuple.first
  3187                                  |> queryView
  3188  
  3189                          hasSpinner =
  3190                              Query.children []
  3191                                  >> Query.first
  3192                                  >> Query.children []
  3193                                  >> Query.index -1
  3194                                  >> Query.has [ style "animation" "container-rotate 1568ms linear infinite" ]
  3195  
  3196                          hasIcon image =
  3197                              Query.children []
  3198                                  >> Query.first
  3199                                  >> Query.children []
  3200                                  >> Query.index -1
  3201                                  >> Query.has
  3202                                      (iconSelector
  3203                                          { size = "28px"
  3204                                          , image = image
  3205                                          }
  3206                                          ++ [ style "background-size" "14px 14px" ]
  3207                                      )
  3208                      in
  3209                      [ describe "status bar"
  3210                          [ test "lays out horizontally and spreads its children" <|
  3211                              \_ ->
  3212                                  givenBuildWithStatus Concourse.BuildStatus.BuildStatusSucceeded Data.httpUnauthorized
  3213                                      |> Query.find [ class "resource-check-status-summary" ]
  3214                                      |> Query.has
  3215                                          [ style "display" "flex"
  3216                                          , style "justify-content" "space-between"
  3217                                          ]
  3218                          , test "fills out the check bar and centers children" <|
  3219                              \_ ->
  3220                                  givenBuildWithStatus Concourse.BuildStatus.BuildStatusSucceeded Data.httpUnauthorized
  3221                                      |> Query.find [ class "resource-check-status-summary" ]
  3222                                      |> Query.has
  3223                                          [ style "align-items" "center"
  3224                                          , style "height" "28px"
  3225                                          , style "flex-grow" "1"
  3226                                          , style "padding-left" "6px"
  3227                                          ]
  3228                          , test "has a dark grey background" <|
  3229                              \_ ->
  3230                                  givenBuildWithStatus Concourse.BuildStatus.BuildStatusSucceeded Data.httpUnauthorized
  3231                                      |> Query.find [ class "resource-check-status-summary" ]
  3232                                      |> Query.has
  3233                                          [ style "background" "#1e1d1d" ]
  3234                          ]
  3235                      , test "when a 401 is received, shows summary instead of build output" <|
  3236                          \_ ->
  3237                              givenBuildWithStatus Concourse.BuildStatus.BuildStatusSucceeded Data.httpUnauthorized
  3238                                  |> Query.find [ class "resource-check-status" ]
  3239                                  |> Expect.all
  3240                                      [ Query.has [ class "resource-check-status-summary" ]
  3241                                      , Query.hasNot [ class "steps" ]
  3242                                      ]
  3243                      , test "when a 403 is received, shows summary instead of build output" <|
  3244                          \_ ->
  3245                              givenBuildWithStatus Concourse.BuildStatus.BuildStatusSucceeded Data.httpForbidden
  3246                                  |> Query.find [ class "resource-check-status" ]
  3247                                  |> Expect.all
  3248                                      [ Query.has [ class "resource-check-status-summary" ]
  3249                                      , Query.hasNot [ class "steps" ]
  3250                                      ]
  3251                      , test "succeeded build status" <|
  3252                          \_ ->
  3253                              givenBuildWithStatus Concourse.BuildStatus.BuildStatusSucceeded Data.httpUnauthorized
  3254                                  |> Query.find [ class "resource-check-status" ]
  3255                                  |> Expect.all
  3256                                      [ Query.has
  3257                                          [ containing [ text "check succeeded" ]
  3258                                          ]
  3259                                      , hasIcon Assets.SuccessCheckIcon
  3260                                      ]
  3261                      , test "failed build status" <|
  3262                          \_ ->
  3263                              givenBuildWithStatus Concourse.BuildStatus.BuildStatusFailed Data.httpUnauthorized
  3264                                  |> Query.find [ class "resource-check-status" ]
  3265                                  |> Expect.all
  3266                                      [ Query.has
  3267                                          [ containing [ text "check failed" ]
  3268                                          ]
  3269                                      , hasIcon Assets.FailureTimesIcon
  3270                                      ]
  3271                      , test "started build status" <|
  3272                          \_ ->
  3273                              givenBuildWithStatus Concourse.BuildStatus.BuildStatusStarted Data.httpUnauthorized
  3274                                  |> Query.find [ class "resource-check-status" ]
  3275                                  |> Expect.all
  3276                                      [ Query.has
  3277                                          [ containing [ text "check in progress" ]
  3278                                          ]
  3279                                      , hasSpinner
  3280                                      ]
  3281                      , test "errored build status" <|
  3282                          \_ ->
  3283                              givenBuildWithStatus Concourse.BuildStatus.BuildStatusErrored Data.httpUnauthorized
  3284                                  |> Query.find [ class "resource-check-status" ]
  3285                                  |> Expect.all
  3286                                      [ Query.has
  3287                                          [ containing [ text "check errored" ]
  3288                                          ]
  3289                                      , hasIcon Assets.ExclamationTriangleIcon
  3290                                      ]
  3291                      , test "aborted build status" <|
  3292                          \_ ->
  3293                              givenBuildWithStatus Concourse.BuildStatus.BuildStatusAborted Data.httpUnauthorized
  3294                                  |> Query.find [ class "resource-check-status" ]
  3295                                  |> Expect.all
  3296                                      [ Query.has
  3297                                          [ containing [ text "check aborted" ]
  3298                                          ]
  3299                                      , hasIcon Assets.InterruptedIcon
  3300                                      ]
  3301                      , test "pending build status" <|
  3302                          \_ ->
  3303                              givenBuildWithStatus Concourse.BuildStatus.BuildStatusPending Data.httpUnauthorized
  3304                                  |> Query.find [ class "resource-check-status" ]
  3305                                  |> Expect.all
  3306                                      [ Query.has
  3307                                          [ containing [ text "check pending" ]
  3308                                          ]
  3309                                      , hasIcon Assets.PendingIcon
  3310                                      ]
  3311                      ]
  3312                  ]
  3313              , describe "build output" <|
  3314                  [ test "when build plan is available, summary is not shown" <|
  3315                      \_ ->
  3316                          init
  3317                              |> Application.handleCallback
  3318                                  (Callback.ResourceFetched (Ok resource))
  3319                              |> Tuple.first
  3320                              |> Application.handleCallback
  3321                                  (Callback.PlanAndResourcesFetched 1 <|
  3322                                      Ok <|
  3323                                          ( { id = "plan"
  3324                                            , step = Concourse.BuildStepCheck "some-resource"
  3325                                            }
  3326                                          , { inputs = [], outputs = [] }
  3327                                          )
  3328                                  )
  3329                              |> Tuple.first
  3330                              |> queryView
  3331                              |> Query.find [ class "resource-check-status" ]
  3332                              |> Query.hasNot [ class "resource-check-status-summary" ]
  3333                  , test "toggling step initialization output" <|
  3334                      \_ ->
  3335                          init
  3336                              |> Application.handleCallback
  3337                                  (Callback.ResourceFetched (Ok resource))
  3338                              |> Tuple.first
  3339                              |> Application.handleCallback
  3340                                  (Callback.PlanAndResourcesFetched 1 <|
  3341                                      Ok <|
  3342                                          ( { id = "plan"
  3343                                            , step = Concourse.BuildStepCheck "some-resource"
  3344                                            }
  3345                                          , { inputs = [], outputs = [] }
  3346                                          )
  3347                                  )
  3348                              |> Tuple.first
  3349                              |> Application.handleDelivery
  3350                                  (EventsReceived <|
  3351                                      Ok <|
  3352                                          [ { url = "/api/v1/builds/1/events"
  3353                                            , data =
  3354                                                  STModels.ImageCheck
  3355                                                      { source = ""
  3356                                                      , id = "plan"
  3357                                                      }
  3358                                                      { id = "image"
  3359                                                      , step = Concourse.BuildStepCheck "some-image"
  3360                                                      }
  3361                                            }
  3362                                          , { url = "/api/v1/builds/1/events"
  3363                                            , data = STModels.End
  3364                                            }
  3365                                          ]
  3366                                  )
  3367                              |> Tuple.first
  3368                              |> Application.update
  3369                                  (Msgs.Update <|
  3370                                      Message.Message.Click <|
  3371                                          Message.Message.StepInitialization "plan"
  3372                                  )
  3373                              |> Tuple.first
  3374                              |> queryView
  3375                              |> Query.find [ class "sub-steps" ]
  3376                              |> Query.has
  3377                                  [ containing [ text "check:" ]
  3378                                  , containing [ text "some-image" ]
  3379                                  ]
  3380                  , test "when build is finished, renders build step tree when events are loaded" <|
  3381                      \_ ->
  3382                          init
  3383                              |> Application.handleCallback
  3384                                  (Callback.ResourceFetched (Ok resource))
  3385                              |> Tuple.first
  3386                              |> Application.handleCallback
  3387                                  (Callback.PlanAndResourcesFetched 1 <|
  3388                                      Ok <|
  3389                                          ( { id = "plan"
  3390                                            , step = Concourse.BuildStepCheck "some-resource"
  3391                                            }
  3392                                          , { inputs = [], outputs = [] }
  3393                                          )
  3394                                  )
  3395                              |> Tuple.first
  3396                              |> Application.handleDelivery
  3397                                  (EventsReceived <|
  3398                                      Ok <|
  3399                                          [ { url = "/api/v1/builds/1/events"
  3400                                            , data = STModels.End
  3401                                            }
  3402                                          ]
  3403                                  )
  3404                              |> Tuple.first
  3405                              |> queryView
  3406                              |> Query.find [ class "build-step" ]
  3407                              |> Query.has
  3408                                  [ containing [ text "check:" ]
  3409                                  , containing [ text "some-resource" ]
  3410                                  ]
  3411                  , test "when build is running, live streams build step tree events" <|
  3412                      \_ ->
  3413                          init
  3414                              |> Application.handleCallback
  3415                                  (Callback.ResourceFetched <|
  3416                                      Ok
  3417                                          { resource
  3418                                              | build =
  3419                                                  Just <| Data.build Concourse.BuildStatus.BuildStatusStarted
  3420                                          }
  3421                                  )
  3422                              |> Tuple.first
  3423                              |> Application.handleCallback
  3424                                  (Callback.PlanAndResourcesFetched 1 <|
  3425                                      Ok <|
  3426                                          ( { id = "plan"
  3427                                            , step = Concourse.BuildStepCheck "some-resource"
  3428                                            }
  3429                                          , { inputs = [], outputs = [] }
  3430                                          )
  3431                                  )
  3432                              |> Tuple.first
  3433                              |> Application.handleDelivery
  3434                                  (EventsReceived <|
  3435                                      Ok <|
  3436                                          [ { url = "/api/v1/builds/1/events"
  3437                                            , data = STModels.Log { id = "plan", source = "stderr" } "hello" Nothing
  3438                                            }
  3439                                          ]
  3440                                  )
  3441                              |> Tuple.first
  3442                              |> Application.update
  3443                                  (Msgs.Update <|
  3444                                      Message.Message.Click <|
  3445                                          Message.Message.StepHeader "plan"
  3446                                  )
  3447                              |> Tuple.first
  3448                              |> queryView
  3449                              |> Query.find [ class "build-step" ]
  3450                              |> Query.has
  3451                                  [ containing [ text "check:" ]
  3452                                  , containing [ text "some-resource" ]
  3453                                  , containing [ text "hello" ]
  3454                                  ]
  3455                  , test "when build is running and end event is received, refreshes resource and versions" <|
  3456                      \_ ->
  3457                          init
  3458                              |> Application.handleCallback
  3459                                  (Callback.ResourceFetched <|
  3460                                      Ok
  3461                                          { resource
  3462                                              | build =
  3463                                                  Just <| Data.build Concourse.BuildStatus.BuildStatusStarted
  3464                                          }
  3465                                  )
  3466                              |> Tuple.first
  3467                              |> Application.handleCallback
  3468                                  (Callback.PlanAndResourcesFetched 1 <|
  3469                                      Ok <|
  3470                                          ( { id = "plan"
  3471                                            , step = Concourse.BuildStepCheck "some-resource"
  3472                                            }
  3473                                          , { inputs = [], outputs = [] }
  3474                                          )
  3475                                  )
  3476                              |> Tuple.first
  3477                              |> Application.handleDelivery
  3478                                  (EventsReceived <|
  3479                                      Ok <|
  3480                                          [ { url = "/api/v1/builds/1/events"
  3481                                            , data = STModels.Log { id = "plan", source = "stderr" } "hello" Nothing
  3482                                            }
  3483                                          ]
  3484                                  )
  3485                              |> Tuple.first
  3486                              |> Application.handleDelivery
  3487                                  (EventsReceived <|
  3488                                      Ok <|
  3489                                          [ { url = "/api/v1/builds/1/events"
  3490                                            , data = STModels.End
  3491                                            }
  3492                                          ]
  3493                                  )
  3494                              |> Tuple.second
  3495                              |> Expect.all
  3496                                  [ Common.contains (Effects.FetchResource Data.resourceId)
  3497                                  , Common.contains (Effects.FetchVersionedResources Data.resourceId Resource.startingPage)
  3498                                  ]
  3499                  , test "one tick after hovering step state, fetches viewport so tooltips can render" <|
  3500                      \_ ->
  3501                          init
  3502                              |> Application.handleCallback
  3503                                  (Callback.ResourceFetched (Ok resource))
  3504                              |> Tuple.first
  3505                              |> Application.handleCallback
  3506                                  (Callback.PlanAndResourcesFetched 1 <|
  3507                                      Ok <|
  3508                                          ( { id = "plan"
  3509                                            , step = Concourse.BuildStepCheck "some-resource"
  3510                                            }
  3511                                          , { inputs = [], outputs = [] }
  3512                                          )
  3513                                  )
  3514                              |> Tuple.first
  3515                              |> Application.handleDelivery
  3516                                  (EventsReceived <|
  3517                                      Ok <|
  3518                                          [ { url = "/api/v1/builds/1/events"
  3519                                            , data = STModels.End
  3520                                            }
  3521                                          ]
  3522                                  )
  3523                              |> Tuple.first
  3524                              >> Application.update
  3525                                  (Msgs.Update <|
  3526                                      Message.Message.Hover <|
  3527                                          Just <|
  3528                                              Message.Message.StepState
  3529                                                  "plan"
  3530                                  )
  3531                              >> Tuple.first
  3532                              >> Application.handleDelivery
  3533                                  (ClockTicked OneSecond <|
  3534                                      Time.millisToPosix 1
  3535                                  )
  3536                              >> Tuple.second
  3537                              >> Common.contains
  3538                                  (Effects.GetViewportOf <| Message.Message.StepState "plan")
  3539                  ]
  3540              ]
  3541          ]
  3542  
  3543  
  3544  csrfToken : String
  3545  csrfToken =
  3546      "csrf_token"
  3547  
  3548  
  3549  flags : Application.Flags
  3550  flags =
  3551      { turbulenceImgSrc = ""
  3552      , notFoundImgSrc = ""
  3553      , csrfToken = csrfToken
  3554      , authToken = ""
  3555      , pipelineRunningKeyframes = ""
  3556      }
  3557  
  3558  
  3559  init : Application.Model
  3560  init =
  3561      Common.init
  3562          ("/teams/"
  3563              ++ teamName
  3564              ++ "/pipelines/"
  3565              ++ pipelineName
  3566              ++ "/resources/"
  3567              ++ resourceName
  3568          )
  3569  
  3570  
  3571  update :
  3572      Message.Message.Message
  3573      -> Application.Model
  3574      -> ( Application.Model, List Effects.Effect )
  3575  update =
  3576      Msgs.Update >> Application.update
  3577  
  3578  
  3579  givenUserIsAuthorized : Application.Model -> Application.Model
  3580  givenUserIsAuthorized =
  3581      Application.handleCallback
  3582          (Callback.UserFetched <|
  3583              Ok
  3584                  { id = "test"
  3585                  , userName = "test"
  3586                  , name = "test"
  3587                  , email = "test"
  3588                  , isAdmin = False
  3589                  , teams =
  3590                      Dict.fromList
  3591                          [ ( teamName, [ "member" ] )
  3592                          ]
  3593                  }
  3594          )
  3595          >> Tuple.first
  3596  
  3597  
  3598  givenResourcePinnedStatically : Application.Model -> Application.Model
  3599  givenResourcePinnedStatically =
  3600      Application.handleCallback
  3601          (Callback.ResourceFetched <|
  3602              Ok
  3603                  { teamName = teamName
  3604                  , pipelineName = pipelineName
  3605                  , name = resourceName
  3606                  , lastChecked = Nothing
  3607                  , pinnedVersion = Just (Dict.fromList [ ( "version", version ) ])
  3608                  , pinnedInConfig = True
  3609                  , pinComment = Nothing
  3610                  , icon = Nothing
  3611                  , build = Nothing
  3612                  }
  3613          )
  3614          >> Tuple.first
  3615  
  3616  
  3617  givenResourcePinnedDynamically : Application.Model -> Application.Model
  3618  givenResourcePinnedDynamically =
  3619      Application.handleCallback
  3620          (Callback.ResourceFetched <|
  3621              Ok
  3622                  { teamName = teamName
  3623                  , pipelineName = pipelineName
  3624                  , name = resourceName
  3625                  , lastChecked = Nothing
  3626                  , pinnedVersion = Just (Dict.fromList [ ( "version", version ) ])
  3627                  , pinnedInConfig = False
  3628                  , pinComment = Nothing
  3629                  , icon = Nothing
  3630                  , build = Nothing
  3631                  }
  3632          )
  3633          >> Tuple.first
  3634  
  3635  
  3636  whenResourceLoadsWithPinnedComment : Application.Model -> ( Application.Model, List Effects.Effect )
  3637  whenResourceLoadsWithPinnedComment =
  3638      Application.handleCallback
  3639          (Callback.ResourceFetched <|
  3640              Ok
  3641                  { teamName = teamName
  3642                  , pipelineName = pipelineName
  3643                  , name = resourceName
  3644                  , lastChecked = Nothing
  3645                  , pinnedVersion =
  3646                      Just (Dict.fromList [ ( "version", version ) ])
  3647                  , pinnedInConfig = False
  3648                  , pinComment = Just "some pin comment"
  3649                  , icon = Nothing
  3650                  , build = Nothing
  3651                  }
  3652          )
  3653  
  3654  
  3655  givenResourcePinnedWithComment : Application.Model -> Application.Model
  3656  givenResourcePinnedWithComment =
  3657      whenResourceLoadsWithPinnedComment >> Tuple.first
  3658  
  3659  
  3660  givenResourceIsNotPinned : Application.Model -> Application.Model
  3661  givenResourceIsNotPinned =
  3662      Application.handleCallback
  3663          (Callback.ResourceFetched <|
  3664              Ok
  3665                  { teamName = teamName
  3666                  , pipelineName = pipelineName
  3667                  , name = resourceName
  3668                  , lastChecked = Just (Time.millisToPosix 0)
  3669                  , pinnedVersion = Nothing
  3670                  , pinnedInConfig = False
  3671                  , pinComment = Nothing
  3672                  , icon = Nothing
  3673                  , build = Nothing
  3674                  }
  3675          )
  3676          >> Tuple.first
  3677  
  3678  
  3679  givenResourceHasIcon : Application.Model -> Application.Model
  3680  givenResourceHasIcon =
  3681      Application.handleCallback
  3682          (Callback.ResourceFetched <|
  3683              Ok
  3684                  { teamName = teamName
  3685                  , pipelineName = pipelineName
  3686                  , name = resourceName
  3687                  , lastChecked = Just (Time.millisToPosix 0)
  3688                  , pinnedVersion = Nothing
  3689                  , pinnedInConfig = False
  3690                  , pinComment = Nothing
  3691                  , icon = Just resourceIcon
  3692                  , build = Nothing
  3693                  }
  3694          )
  3695          >> Tuple.first
  3696  
  3697  
  3698  hoverOverPinBar : Application.Model -> Application.Model
  3699  hoverOverPinBar =
  3700      update (Message.Message.Hover <| Just Message.Message.PinBar)
  3701          >> Tuple.first
  3702  
  3703  
  3704  hoverOverPinButton : Application.Model -> Application.Model
  3705  hoverOverPinButton =
  3706      update (Message.Message.Hover <| Just <| Message.Message.PinButton versionID)
  3707          >> Tuple.first
  3708  
  3709  
  3710  clickToPin : Models.VersionId -> Application.Model -> Application.Model
  3711  clickToPin vid =
  3712      update (Message.Message.Click <| Message.Message.PinButton vid)
  3713          >> Tuple.first
  3714  
  3715  
  3716  clickToUnpin : Application.Model -> Application.Model
  3717  clickToUnpin =
  3718      update (Message.Message.Click <| Message.Message.PinButton versionID)
  3719          >> Tuple.first
  3720  
  3721  
  3722  clickToDisable : Models.VersionId -> Application.Model -> Application.Model
  3723  clickToDisable vid =
  3724      update (Message.Message.Click <| Message.Message.VersionToggle vid)
  3725          >> Tuple.first
  3726  
  3727  
  3728  emptyPagination : Pagination
  3729  emptyPagination =
  3730      { nextPage = Nothing, previousPage = Nothing }
  3731  
  3732  
  3733  givenVersionsWithPages : Page -> Concourse.Pagination.Pagination -> Application.Model -> ( Application.Model, List Effects.Effect )
  3734  givenVersionsWithPages requestedPage pagination =
  3735      Application.handleCallback
  3736          (Callback.VersionedResourcesFetched <|
  3737              Ok
  3738                  ( requestedPage
  3739                  , { content =
  3740                          [ { id = versionID.versionID
  3741                            , version = Dict.fromList [ ( "version", version ) ]
  3742                            , metadata = []
  3743                            , enabled = True
  3744                            }
  3745                          , { id = otherVersionID.versionID
  3746                            , version = Dict.fromList [ ( "version", otherVersion ) ]
  3747                            , metadata = []
  3748                            , enabled = True
  3749                            }
  3750                          , { id = disabledVersionID.versionID
  3751                            , version = Dict.fromList [ ( "version", disabledVersion ) ]
  3752                            , metadata = []
  3753                            , enabled = False
  3754                            }
  3755                          ]
  3756                    , pagination = pagination
  3757                    }
  3758                  )
  3759          )
  3760  
  3761  
  3762  givenVersionsWithoutPagination : Application.Model -> Application.Model
  3763  givenVersionsWithoutPagination =
  3764      givenVersionsWithPages Resource.startingPage emptyPagination
  3765          >> Tuple.first
  3766  
  3767  
  3768  givenVersionsWithPagination : Application.Model -> Application.Model
  3769  givenVersionsWithPagination =
  3770      givenVersionsWithPages Resource.startingPage
  3771          { previousPage =
  3772              Just
  3773                  { direction = From 100
  3774                  , limit = 1
  3775                  }
  3776          , nextPage =
  3777              Just
  3778                  { direction = To 1
  3779                  , limit = 1
  3780                  }
  3781          }
  3782          >> Tuple.first
  3783  
  3784  
  3785  givenTextareaFocused : Application.Model -> Application.Model
  3786  givenTextareaFocused =
  3787      update Message.Message.FocusTextArea
  3788          >> Tuple.first
  3789  
  3790  
  3791  givenTextareaBlurred : Application.Model -> Application.Model
  3792  givenTextareaBlurred =
  3793      update Message.Message.BlurTextArea
  3794          >> Tuple.first
  3795  
  3796  
  3797  whenUserEditsComment : Application.Model -> ( Application.Model, List Effects.Effect )
  3798  whenUserEditsComment =
  3799      update (Message.Message.EditComment "foo")
  3800  
  3801  
  3802  givenUserEditedComment : Application.Model -> Application.Model
  3803  givenUserEditedComment =
  3804      whenUserEditsComment
  3805          >> Tuple.first
  3806  
  3807  
  3808  givenUserClicksEditButton : Application.Model -> Application.Model
  3809  givenUserClicksEditButton =
  3810      update
  3811          (Message.Message.Click <|
  3812              Message.Message.EditButton
  3813          )
  3814          >> Tuple.first
  3815  
  3816  
  3817  givenThePipelineIsArchived : Application.Model -> Application.Model
  3818  givenThePipelineIsArchived =
  3819      Application.handleCallback
  3820          (Callback.AllPipelinesFetched <|
  3821              Ok
  3822                  [ Data.pipeline teamName 0
  3823                      |> Data.withName pipelineName
  3824                      |> Data.withArchived True
  3825                  ]
  3826          )
  3827          >> Tuple.first
  3828  
  3829  
  3830  pressEnterKey :
  3831      Application.Model
  3832      -> ( Application.Model, List Effects.Effect )
  3833  pressEnterKey =
  3834      Application.update
  3835          (Msgs.DeliveryReceived <|
  3836              KeyDown
  3837                  { ctrlKey = False
  3838                  , shiftKey = False
  3839                  , metaKey = False
  3840                  , code = Keyboard.Enter
  3841                  }
  3842          )
  3843  
  3844  
  3845  pressControlEnter :
  3846      Application.Model
  3847      -> ( Application.Model, List Effects.Effect )
  3848  pressControlEnter =
  3849      Application.update
  3850          (Msgs.DeliveryReceived <|
  3851              KeyDown
  3852                  { ctrlKey = True
  3853                  , shiftKey = False
  3854                  , metaKey = False
  3855                  , code = Keyboard.Enter
  3856                  }
  3857          )
  3858  
  3859  
  3860  pressMetaEnter :
  3861      Application.Model
  3862      -> ( Application.Model, List Effects.Effect )
  3863  pressMetaEnter =
  3864      Application.update
  3865          (Msgs.DeliveryReceived <|
  3866              KeyDown
  3867                  { ctrlKey = False
  3868                  , shiftKey = False
  3869                  , metaKey = True
  3870                  , code = Keyboard.Enter
  3871                  }
  3872          )
  3873  
  3874  
  3875  commentBar : Application.Model -> Query.Single Msgs.TopLevelMessage
  3876  commentBar =
  3877      queryView
  3878          >> Query.find [ id "comment-bar" ]
  3879  
  3880  
  3881  iconContainer : Application.Model -> Query.Single Msgs.TopLevelMessage
  3882  iconContainer =
  3883      commentBar >> Query.children [] >> Query.first
  3884  
  3885  
  3886  commentPre : Query.Single Msgs.TopLevelMessage
  3887  commentPre =
  3888      init
  3889          |> givenResourcePinnedWithComment
  3890          |> iconContainer
  3891          |> Query.children []
  3892          |> Query.index 1
  3893  
  3894  
  3895  versionSelector : String -> List Selector
  3896  versionSelector v =
  3897      anyVersionSelector ++ [ containing [ text v ] ]
  3898  
  3899  
  3900  anyVersionSelector : List Selector
  3901  anyVersionSelector =
  3902      [ tag "li" ]
  3903  
  3904  
  3905  pinButtonSelector : List Selector
  3906  pinButtonSelector =
  3907      [ attribute (Attr.attribute "aria-label" "Pin Resource Version") ]
  3908  
  3909  
  3910  pointerCursor : List Selector
  3911  pointerCursor =
  3912      [ style "cursor" "pointer" ]
  3913  
  3914  
  3915  defaultCursor : List Selector
  3916  defaultCursor =
  3917      [ style "cursor" "default" ]
  3918  
  3919  
  3920  checkboxSelector : List Selector
  3921  checkboxSelector =
  3922      [ attribute (Attr.attribute "aria-label" "Toggle Resource Version Enabled") ]
  3923  
  3924  
  3925  hasCheckbox : Query.Single msg -> Expectation
  3926  hasCheckbox =
  3927      Query.findAll checkboxSelector
  3928          >> Query.count (Expect.equal 1)
  3929  
  3930  
  3931  purpleOutlineSelector : List Selector
  3932  purpleOutlineSelector =
  3933      [ style "border" <| "1px solid " ++ purpleHex ]
  3934  
  3935  
  3936  findLast : List Selector -> Query.Single msg -> Query.Single msg
  3937  findLast selectors =
  3938      Query.findAll selectors >> Query.index -1
  3939  
  3940  
  3941  pinBarTooltipSelector : List Selector
  3942  pinBarTooltipSelector =
  3943      [ id "pin-bar-tooltip" ]
  3944  
  3945  
  3946  versionTooltipSelector : List Selector
  3947  versionTooltipSelector =
  3948      [ style "position" "absolute"
  3949      , style "bottom" "25px"
  3950      , style "background-color" ColorValues.grey20
  3951      , style "z-index" "2"
  3952      , style "padding" "5px"
  3953      , style "width" "170px"
  3954      , containing [ text "enable via pipeline config" ]
  3955      ]
  3956  
  3957  
  3958  pinButtonHasTransitionState : Query.Single msg -> Expectation
  3959  pinButtonHasTransitionState =
  3960      Expect.all
  3961          [ Query.has loadingSpinnerSelector
  3962          , Query.hasNot
  3963              [ style "background-image" <|
  3964                  Assets.backgroundImage <|
  3965                      Just Assets.PinIconWhite
  3966              ]
  3967          ]
  3968  
  3969  
  3970  pinButtonHasUnpinnedState : Query.Single msg -> Expectation
  3971  pinButtonHasUnpinnedState =
  3972      Expect.all
  3973          [ Query.has
  3974              [ style "background-image" <|
  3975                  Assets.backgroundImage <|
  3976                      Just Assets.PinIconWhite
  3977              ]
  3978          , Query.hasNot purpleOutlineSelector
  3979          ]
  3980  
  3981  
  3982  pinToolsHasTransitionState : Query.Single msg -> Expectation
  3983  pinToolsHasTransitionState =
  3984      Query.find [ id "pin-tools" ]
  3985          >> Expect.all
  3986              [ Query.has [ style "border" <| "1px solid " ++ lightGreyHex ]
  3987              , Query.findAll
  3988                  [ style "background-image" <|
  3989                      Assets.backgroundImage <|
  3990                          Just Assets.PinIconGrey
  3991                  ]
  3992                  >> Query.count (Expect.equal 1)
  3993              , Query.hasNot [ tag "table" ]
  3994              ]
  3995  
  3996  
  3997  pinBarHasPinnedState : String -> Query.Single msg -> Expectation
  3998  pinBarHasPinnedState v =
  3999      Query.find [ id "pin-bar" ]
  4000          >> Expect.all
  4001              [ Query.has [ text v ]
  4002              , Query.findAll
  4003                  [ style "background-image" <|
  4004                      Assets.backgroundImage <|
  4005                          Just Assets.PinIconWhite
  4006                  ]
  4007                  >> Query.count (Expect.equal 1)
  4008              ]
  4009  
  4010  
  4011  loadingSpinnerSelector : List Selector
  4012  loadingSpinnerSelector =
  4013      [ style "animation" "container-rotate 1568ms linear infinite"
  4014      , style "height" "12.5px"
  4015      , style "width" "12.5px"
  4016      , style "margin" "6.25px"
  4017      ]
  4018  
  4019  
  4020  checkboxHasTransitionState : Query.Single msg -> Expectation
  4021  checkboxHasTransitionState =
  4022      Expect.all
  4023          [ Query.has loadingSpinnerSelector
  4024          , Query.hasNot
  4025              [ style "background-image" <|
  4026                  Assets.backgroundImage <|
  4027                      Just Assets.CheckmarkIcon
  4028              ]
  4029          ]
  4030  
  4031  
  4032  checkboxHasDisabledState : Query.Single msg -> Expectation
  4033  checkboxHasDisabledState =
  4034      Expect.all
  4035          [ Query.hasNot loadingSpinnerSelector
  4036          , Query.hasNot
  4037              [ style "background-image" <|
  4038                  Assets.backgroundImage <|
  4039                      Just Assets.CheckmarkIcon
  4040              ]
  4041          ]
  4042  
  4043  
  4044  checkboxHasEnabledState : Query.Single msg -> Expectation
  4045  checkboxHasEnabledState =
  4046      Expect.all
  4047          [ Query.hasNot loadingSpinnerSelector
  4048          , Query.has
  4049              [ style "background-image" <|
  4050                  Assets.backgroundImage <|
  4051                      Just Assets.CheckmarkIcon
  4052              ]
  4053          ]
  4054  
  4055  
  4056  versionHasDisabledState : Query.Single msg -> Expectation
  4057  versionHasDisabledState =
  4058      Expect.all
  4059          [ Query.has [ style "opacity" "0.5" ]
  4060          , Query.find checkboxSelector
  4061              >> checkboxHasDisabledState
  4062          ]
  4063  
  4064  
  4065  session : Session
  4066  session =
  4067      { userState =
  4068          UserStateLoggedIn
  4069              { id = "test"
  4070              , userName = "test"
  4071              , name = "test"
  4072              , email = "test"
  4073              , isAdmin = False
  4074              , teams = Dict.fromList [ ( teamName, [ "member" ] ) ]
  4075              }
  4076      , hovered = HoverState.NoHover
  4077      , clusterName = ""
  4078      , version = ""
  4079      , turbulenceImgSrc = flags.turbulenceImgSrc
  4080      , notFoundImgSrc = flags.notFoundImgSrc
  4081      , csrfToken = flags.csrfToken
  4082      , authToken = flags.authToken
  4083      , pipelineRunningKeyframes = flags.pipelineRunningKeyframes
  4084      , expandedTeamsInAllPipelines = Set.empty
  4085      , collapsedTeamsInFavorites = Set.empty
  4086      , pipelines = RemoteData.NotAsked
  4087      , sideBarState =
  4088          { isOpen = False
  4089          , width = 275
  4090          }
  4091      , draggingSideBar = False
  4092      , favoritedPipelines = Set.empty
  4093      , screenSize = ScreenSize.Desktop
  4094      , timeZone = Time.utc
  4095      }