github.com/tilt-dev/tilt@v0.36.0/web/src/BulkApiButton.test.tsx (about)

     1  import { render, screen, waitFor } from "@testing-library/react"
     2  import userEvent from "@testing-library/user-event"
     3  import { ApiButtonToggleState, ApiButtonType } from "./ApiButton"
     4  import fetchMock from "fetch-mock"
     5  import {
     6    getUIButtonDataFromCall,
     7    mockUIButtonUpdates,
     8  } from "./ApiButton.testhelpers"
     9  import {
    10    BulkApiButton,
    11    canBulkButtonBeToggled,
    12    canButtonBeToggled,
    13  } from "./BulkApiButton"
    14  import { BulkAction } from "./OverviewTableBulkActions"
    15  import { flushPromises } from "./promise"
    16  import { disableButton, oneUIButton } from "./testdata"
    17  
    18  const NON_TOGGLE_BUTTON = oneUIButton({ componentID: "database" })
    19  const DISABLE_BUTTON_DB = disableButton("database", true)
    20  const DISABLE_BUTTON_FRONTEND = disableButton("frontend", true)
    21  const ENABLE_BUTTON_BACKEND = disableButton("backend", false)
    22  const TEST_UIBUTTONS = [
    23    DISABLE_BUTTON_DB,
    24    DISABLE_BUTTON_FRONTEND,
    25    ENABLE_BUTTON_BACKEND,
    26  ]
    27  
    28  describe("BulkApiButton", () => {
    29    beforeEach(() => {
    30      mockUIButtonUpdates()
    31    })
    32  
    33    afterEach(() => {
    34      fetchMock.reset()
    35    })
    36  
    37    it("is disabled when there are no UIButtons", () => {
    38      render(
    39        <BulkApiButton
    40          bulkAction={BulkAction.Disable}
    41          buttonText="I cannot be clicked"
    42          requiresConfirmation={false}
    43          uiButtons={[]}
    44        />
    45      )
    46  
    47      const bulkButton = screen.getByLabelText("Trigger I cannot be clicked")
    48  
    49      expect(bulkButton).toBeTruthy()
    50      expect((bulkButton as HTMLButtonElement).disabled).toBe(true)
    51    })
    52  
    53    it("is disabled when there are no UIButtons that can be toggled to the target toggle state", () => {
    54      render(
    55        <BulkApiButton
    56          bulkAction={BulkAction.Disable}
    57          buttonText="I cannot be toggled that way"
    58          requiresConfirmation={false}
    59          targetToggleState={ApiButtonToggleState.On}
    60          uiButtons={[DISABLE_BUTTON_DB]}
    61        />
    62      )
    63  
    64      const bulkButton = screen.getByLabelText(
    65        "Trigger I cannot be toggled that way"
    66      )
    67  
    68      expect(bulkButton).toBeTruthy()
    69      expect((bulkButton as HTMLButtonElement).disabled).toBe(true)
    70    })
    71  
    72    it("is enabled when there are UIButtons", () => {
    73      render(
    74        <BulkApiButton
    75          bulkAction={BulkAction.Disable}
    76          buttonText="Run lint"
    77          requiresConfirmation={false}
    78          uiButtons={[NON_TOGGLE_BUTTON]}
    79        />
    80      )
    81  
    82      const bulkButton = screen.getByLabelText("Trigger Run lint")
    83  
    84      expect(bulkButton).toBeTruthy()
    85      expect((bulkButton as HTMLButtonElement).disabled).toBe(false)
    86    })
    87  
    88    it("is enabled when there are UIButtons that can be toggled to the target toggle state", () => {
    89      render(
    90        <BulkApiButton
    91          bulkAction={BulkAction.Disable}
    92          buttonText="Enable resources"
    93          requiresConfirmation={false}
    94          targetToggleState={ApiButtonToggleState.On}
    95          uiButtons={[DISABLE_BUTTON_DB, ENABLE_BUTTON_BACKEND]}
    96        />
    97      )
    98  
    99      const bulkButton = screen.getByLabelText("Trigger Enable resources")
   100  
   101      expect(bulkButton).toBeTruthy()
   102      expect((bulkButton as HTMLButtonElement).disabled).toBe(false)
   103    })
   104  
   105    describe("when it's clicked", () => {
   106      let mockCallback: jest.Mock
   107  
   108      beforeEach(async () => {
   109        mockCallback = jest.fn()
   110        render(
   111          <BulkApiButton
   112            bulkAction={BulkAction.Disable}
   113            buttonText="Turn everything off"
   114            onClickCallback={mockCallback}
   115            requiresConfirmation={false}
   116            targetToggleState={ApiButtonToggleState.Off}
   117            uiButtons={TEST_UIBUTTONS}
   118          />
   119        )
   120  
   121        const bulkButton = screen.getByLabelText("Trigger Turn everything off")
   122        userEvent.click(bulkButton)
   123  
   124        // Wait for the async calls to complete
   125        await waitFor(flushPromises)
   126      })
   127  
   128      it("triggers all buttons that can be toggled when it's clicked", () => {
   129        const buttonUpdateCalls = fetchMock.calls()
   130  
   131        // Out of the three test buttons, only two of them can be toggled to the target toggle state
   132        expect(buttonUpdateCalls.length).toBe(2)
   133  
   134        const buttonUpdateNames = buttonUpdateCalls.map(
   135          (call) => getUIButtonDataFromCall(call)?.metadata?.name
   136        )
   137  
   138        expect(buttonUpdateNames).toStrictEqual([
   139          DISABLE_BUTTON_DB.metadata?.name,
   140          DISABLE_BUTTON_FRONTEND.metadata?.name,
   141        ])
   142      })
   143  
   144      it("calls a specified onClick callback", () => {
   145        expect(mockCallback).toHaveBeenCalledTimes(1)
   146      })
   147    })
   148  
   149    describe("when it requires confirmation", () => {
   150      beforeEach(async () => {
   151        render(
   152          <BulkApiButton
   153            bulkAction={BulkAction.Disable}
   154            buttonText="Click everything when I'm sure"
   155            requiresConfirmation={true}
   156            uiButtons={TEST_UIBUTTONS}
   157          />
   158        )
   159  
   160        const bulkButton = screen.getByLabelText(
   161          "Trigger Click everything when I'm sure"
   162        )
   163        userEvent.click(bulkButton)
   164      })
   165  
   166      it("displays confirm and cancel buttons when clicked once", () => {
   167        expect(
   168          screen.getByLabelText("Confirm Click everything when I'm sure")
   169        ).toBeTruthy()
   170        expect(
   171          screen.getByLabelText("Cancel Click everything when I'm sure")
   172        ).toBeTruthy()
   173      })
   174  
   175      it("triggers all buttons when `Confirm` is clicked", async () => {
   176        userEvent.click(
   177          screen.getByLabelText("Confirm Click everything when I'm sure")
   178        )
   179  
   180        await waitFor(flushPromises)
   181  
   182        const buttonUpdateCalls = fetchMock.calls()
   183        expect(buttonUpdateCalls.length).toBe(3)
   184  
   185        const buttonUpdateNames = buttonUpdateCalls.map(
   186          (call) => getUIButtonDataFromCall(call)?.metadata?.name
   187        )
   188  
   189        expect(buttonUpdateNames).toStrictEqual([
   190          DISABLE_BUTTON_DB.metadata?.name,
   191          DISABLE_BUTTON_FRONTEND.metadata?.name,
   192          ENABLE_BUTTON_BACKEND.metadata?.name,
   193        ])
   194      })
   195  
   196      it("does NOT trigger any buttons when `Cancel` is clicked", async () => {
   197        userEvent.click(
   198          screen.getByLabelText("Cancel Click everything when I'm sure")
   199        )
   200  
   201        // There shouldn't be any async calls made when canceling, but wait in case
   202        await waitFor(flushPromises)
   203  
   204        expect(
   205          screen.getByLabelText("Trigger Click everything when I'm sure")
   206        ).toBeTruthy()
   207      })
   208    })
   209  
   210    describe("helpers", () => {
   211      describe("canButtonBeToggled", () => {
   212        it("returns true when there is no target toggle state", () => {
   213          expect(canButtonBeToggled(DISABLE_BUTTON_FRONTEND)).toBe(true)
   214        })
   215  
   216        it("returns false when button is not a toggle button", () => {
   217          expect(canButtonBeToggled(NON_TOGGLE_BUTTON)).toBe(false)
   218        })
   219  
   220        it("returns false when button is already in the target toggle state", () => {
   221          expect(
   222            canButtonBeToggled(DISABLE_BUTTON_FRONTEND, ApiButtonToggleState.On)
   223          ).toBe(false)
   224          expect(
   225            canButtonBeToggled(ENABLE_BUTTON_BACKEND, ApiButtonToggleState.Off)
   226          ).toBe(false)
   227        })
   228  
   229        it("returns true when button is not in the target toggle state", () => {
   230          expect(
   231            canButtonBeToggled(DISABLE_BUTTON_FRONTEND, ApiButtonToggleState.Off)
   232          ).toBe(true)
   233          expect(
   234            canButtonBeToggled(ENABLE_BUTTON_BACKEND, ApiButtonToggleState.On)
   235          ).toBe(true)
   236        })
   237      })
   238  
   239      describe("canBulkButtonBeToggled", () => {
   240        it("returns false if no buttons in the list of buttons can be toggled", () => {
   241          expect(
   242            canBulkButtonBeToggled(
   243              [NON_TOGGLE_BUTTON, DISABLE_BUTTON_FRONTEND],
   244              ApiButtonToggleState.On
   245            )
   246          ).toBe(false)
   247        })
   248  
   249        it("returns true if at least one button in the list of buttons can be toggled", () => {
   250          expect(
   251            canBulkButtonBeToggled(
   252              [NON_TOGGLE_BUTTON, DISABLE_BUTTON_FRONTEND, ENABLE_BUTTON_BACKEND],
   253              ApiButtonToggleState.On
   254            )
   255          ).toBe(true)
   256        })
   257      })
   258    })
   259  })