import {
  DataSteps,
  FlowResponseDataEnum,
  FlowSourceEnum,
  TransformationSteps,
  HttpAssertions as CoreAssertions,
  IHttpStep,
} from '@api-client/core/browser.js'
import { assert, fixture, html } from '@open-wc/testing'
import { resetMouse, setViewport } from '@web/test-runner-commands'
import sinon from 'sinon'
import type HttpAssertionsElement from '../../../src/elements/http/HttpAssertions.element.js'
import type UiChip from '../../../src/md/chip/internals/Chip.js'
import type Input from '../../../src/md/input/Input.js'
import { UiMock } from '../../helpers/UiMock.js'
import '../../../src/define/http/http-assertions.js'

describe('element', () => {
  describe('http', () => {
    before(async () => {
      await setViewport({ width: 1920, height: 1080 })
    })

    describe('HttpAssertions', () => {
      async function basicFixture(): Promise<HttpAssertionsElement> {
        return fixture(html`<http-assertions></http-assertions>`)
      }

      describe('initialization', () => {
        it('has no assertions UI', async () => {
          const editor = await basicFixture()
          const nodes = editor.shadowRoot!.querySelectorAll('.param-row')
          assert.lengthOf(nodes, 0, 'has no param-row nodes')
        })

        it('has the assertion options panel', async () => {
          const editor = await basicFixture()
          const node = editor.shadowRoot!.querySelector('.options')
          assert.ok(node)
        })

        it('has the empty message rendered', async () => {
          const editor = await basicFixture()
          const node = editor.shadowRoot!.querySelector('.empty-message')!
          assert.ok(node, 'has the node')
          assert.equal(node.textContent?.trim(), 'No assertions added')
        })
      })

      describe('addAssertion()', () => {
        it('adds a new assertions', async () => {
          const editor = await basicFixture()
          editor.addAssertion()
          assert.lengthOf(editor.model, 1, 'has the added assertions')
          const [info] = editor.model
          assert.isTrue(info.enabled, 'the assertion is enabled by default')
          assert.isUndefined(info.description, 'the assertion has no description')
          assert.deepEqual(info.steps, [], 'the assertion has empty steps')
        })

        it('does not notify change when adding an empty assertion', async () => {
          const editor = await basicFixture()
          const spy = sinon.spy()
          editor.addEventListener('change', spy)
          editor.addAssertion()
          assert.isFalse(spy.called)
        })

        it('schedules an update', async () => {
          const editor = await basicFixture()
          editor.addAssertion()
          assert.isTrue(editor.isUpdatePending)
        })

        it('renders the assertion', async () => {
          const editor = await basicFixture()
          editor.addAssertion()
          await editor.updateComplete
          const nodes = editor.shadowRoot!.querySelectorAll('.param-row')
          assert.lengthOf(nodes, 1)
        })

        it('adds the assertion when clicking on the add button', async () => {
          const editor = await basicFixture()
          const button = editor.shadowRoot!.querySelector('.editor-actions .add-button') as HTMLElement
          assert.ok(button, 'has the button')
          button.click()
          assert.lengthOf(editor.model, 1, 'has the added assertions')
        })
      })

      describe('removeAssertion()', () => {
        let editor: HttpAssertionsElement
        beforeEach(async () => {
          editor = await basicFixture()
          editor.addAssertion()
        })

        it('removes the assertion', () => {
          editor.removeAssertion(0)
          assert.deepEqual(editor.model, [])
        })

        it('throws when the index is out of bounds', () => {
          assert.throws(
            () => {
              editor.removeAssertion(1)
            },
            RangeError,
            'Invalid index: 1'
          )
        })

        it('removes the specific assertion', () => {
          editor.addAssertion()
          editor.addAssertion()
          editor.model[0].description = '0'
          editor.model[1].description = '1'
          editor.model[2].description = '2'
          editor.removeAssertion(1)
          assert.deepEqual(editor.model[0].description, '0')
          assert.deepEqual(editor.model[1].description, '2')
        })

        it('notifies about the change', async () => {
          const spy = sinon.spy()
          editor.addEventListener('change', spy)
          editor.removeAssertion(0)
          assert.isTrue(spy.calledOnce)
        })

        it('schedules an update', async () => {
          editor.removeAssertion(0)
          assert.isTrue(editor.isUpdatePending)
        })

        it('removes the assertion when clicking on the delete button', async () => {
          const button = editor.shadowRoot!.querySelector('.param-row .delete-button') as HTMLElement
          assert.ok(button, 'has the button')
          button.click()
          await editor.updateComplete
          assert.deepEqual(editor.model, [], 'model is empty')
          const nodes = editor.shadowRoot!.querySelectorAll('.param-row')
          assert.lengthOf(nodes, 0, 'Has no rows rendered')
        })
      })

      describe('toggleAssertion()', () => {
        let editor: HttpAssertionsElement
        beforeEach(async () => {
          editor = await basicFixture()
          editor.addAssertion()
        })

        it('toggle the assertion', () => {
          editor.toggleAssertion(0, false)
          assert.isFalse(editor.model[0].enabled)
        })

        it('throws when the index is out of bounds', () => {
          assert.throws(
            () => {
              editor.toggleAssertion(1, false)
            },
            RangeError,
            'Invalid index: 1'
          )
        })

        it('notifies about the change', async () => {
          const spy = sinon.spy()
          editor.addEventListener('change', spy)
          editor.toggleAssertion(0, false)
          assert.isTrue(spy.calledOnce)
        })

        it('schedules an update', async () => {
          editor.toggleAssertion(0, false)
          assert.isTrue(editor.isUpdatePending)
        })

        it('toggles the assertion when clicking on the toggle button', async () => {
          const button = editor.shadowRoot!.querySelector('.param-row .toggle-button') as HTMLElement
          assert.ok(button, 'has the button')
          button.click()
          assert.isFalse(editor.model[0].enabled)
        })
      })

      describe('Data source chips', () => {
        describe('chips rendering', () => {
          let editor: HttpAssertionsElement
          beforeEach(async () => {
            editor = await basicFixture()
            editor.addAssertion()
          })

          it('renders the status code', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadDataSourceStepKind}"][data-data="${FlowResponseDataEnum.status}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'data-source', 'has the group')
            assert.equal(chip.dataset.source, FlowSourceEnum.response, 'has the source')
            assert.equal(chip.innerText.trim(), 'Status code', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the response headers', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadDataSourceStepKind}"][data-data="${FlowResponseDataEnum.headers}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'data-source', 'has the group')
            assert.equal(chip.dataset.source, FlowSourceEnum.response, 'has the source')
            assert.equal(chip.innerText.trim(), 'Response headers', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the response body', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadDataSourceStepKind}"][data-data="${FlowResponseDataEnum.body}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'data-source', 'has the group')
            assert.equal(chip.dataset.source, FlowSourceEnum.response, 'has the source')
            assert.equal(chip.innerText.trim(), 'Response body', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the status message', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadDataSourceStepKind}"][data-data="${FlowResponseDataEnum.statusText}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'data-source', 'has the group')
            assert.equal(chip.dataset.source, FlowSourceEnum.response, 'has the source')
            assert.equal(chip.innerText.trim(), 'Status message', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })
        })

        describe('drag and drop', () => {
          let editor: HttpAssertionsElement
          beforeEach(async () => {
            editor = await basicFixture()
            editor.addAssertion()
          })

          afterEach(async () => {
            await resetMouse()
          })

          it('drops the status code source onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadDataSourceStepKind}"][data-data="${FlowResponseDataEnum.status}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as DataSteps.IReadDataSourceStep

            assert.equal(step.kind, DataSteps.ReadDataSourceStepKind, 'has the kind')
            assert.equal(step.source, FlowSourceEnum.response, 'has the source')
            assert.equal(step.data, FlowResponseDataEnum.status, 'has the data')

            const unit = target.querySelector('.step-unit')!
            assert.ok(unit, 'renders the step container')
            const spacer = unit.querySelector('.drop-target.spacer')!
            assert.ok(spacer, 'renders the drop target')
            const pill = unit.querySelector('.step-pill')!
            assert.ok(pill, 'renders the data pill')
            assert.equal(pill.textContent?.trim(), 'Status code', 'renders the data pill value')
            assert.isTrue(spacer.nextElementSibling === pill, 'render the drop target before the pill')
          })

          it('drops the status message source onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadDataSourceStepKind}"][data-data="${FlowResponseDataEnum.statusText}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as DataSteps.IReadDataSourceStep

            assert.equal(step.kind, DataSteps.ReadDataSourceStepKind, 'has the kind')
            assert.equal(step.source, FlowSourceEnum.response, 'has the source')
            assert.equal(step.data, FlowResponseDataEnum.statusText, 'has the data')

            const unit = target.querySelector('.step-unit')!
            assert.ok(unit, 'renders the step container')
            const spacer = unit.querySelector('.drop-target.spacer')!
            assert.ok(spacer, 'renders the drop target')
            const pill = unit.querySelector('.step-pill')!
            assert.ok(pill, 'renders the data pill')
            assert.equal(pill.textContent?.trim(), 'Status message', 'renders the data pill value')
            assert.isTrue(spacer.nextElementSibling === pill, 'render the drop target before the pill')
          })

          it('drops the status message source onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadDataSourceStepKind}"][data-data="${FlowResponseDataEnum.headers}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as DataSteps.IReadDataSourceStep

            assert.equal(step.kind, DataSteps.ReadDataSourceStepKind, 'has the kind')
            assert.equal(step.source, FlowSourceEnum.response, 'has the source')
            assert.equal(step.data, FlowResponseDataEnum.headers, 'has the data')

            const unit = target.querySelector('.step-unit')!
            assert.ok(unit, 'renders the step container')
            const spacer = unit.querySelector('.drop-target.spacer')!
            assert.ok(spacer, 'renders the drop target')
            const pill = unit.querySelector('.step-pill')!
            assert.ok(pill, 'renders the data pill')
            assert.equal(pill.textContent?.trim(), 'Response headers', 'renders the data pill value')
            assert.isTrue(spacer.nextElementSibling === pill, 'render the drop target before the pill')
          })

          it('drops the status message source onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadDataSourceStepKind}"][data-data="${FlowResponseDataEnum.body}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as DataSteps.IReadDataSourceStep

            assert.equal(step.kind, DataSteps.ReadDataSourceStepKind, 'has the kind')
            assert.equal(step.source, FlowSourceEnum.response, 'has the source')
            assert.equal(step.data, FlowResponseDataEnum.body, 'has the data')

            const unit = target.querySelector('.step-unit')!
            assert.ok(unit, 'renders the step container')
            const spacer = unit.querySelector('.drop-target.spacer')!
            assert.ok(spacer, 'renders the drop target')
            const pill = unit.querySelector('.step-pill')!
            assert.ok(pill, 'renders the data pill')
            assert.equal(pill.textContent?.trim(), 'Response body', 'renders the data pill value')
            assert.isTrue(spacer.nextElementSibling === pill, 'render the drop target before the pill')
          })

          it('drops the data source onto a drop target', async () => {
            editor.model = [
              {
                steps: [new DataSteps.ReadValueStep().toJSON(), new CoreAssertions.EqualAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadDataSourceStepKind}"][data-data="${FlowResponseDataEnum.status}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps [data-step-index="0"]`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 3, 'has the added step')
            const step = editor.model[0].steps[0] as DataSteps.IReadDataSourceStep

            assert.equal(step.kind, DataSteps.ReadDataSourceStepKind, 'puts the added step at the position')
          })
        })
      })

      describe('Transformation chips', () => {
        describe('chips rendering', () => {
          let editor: HttpAssertionsElement
          beforeEach(async () => {
            editor = await basicFixture()
            editor.addAssertion()
          })

          it('renders the read value', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadValueStepKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'transformation', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Read value', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the as number', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.AsNumberStepKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'transformation', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'As number', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the round', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.RoundStepKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'transformation', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Round', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the to lower case', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.AsLowerCaseStepKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'transformation', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'To lower case', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the to upper case', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.AsUpperCaseStepKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'transformation', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'To upper case', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the trim', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.TrimStepKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'transformation', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Trim', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the substring', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.SubstringStepKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'transformation', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Substring', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })
        })

        describe('drag and drop', () => {
          let editor: HttpAssertionsElement
          beforeEach(async () => {
            editor = await basicFixture()
            editor.addAssertion()
          })

          afterEach(async () => {
            await resetMouse()
          })

          it('drops the read value onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadValueStepKind}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as DataSteps.IReadValueStep

            assert.equal(step.kind, DataSteps.ReadValueStepKind, 'has the kind')
          })

          it('drops the as number value onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.AsNumberStepKind}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as TransformationSteps.IAsNumberStep

            assert.equal(step.kind, TransformationSteps.AsNumberStepKind, 'has the kind')
          })

          it('drops the round onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.RoundStepKind}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as TransformationSteps.IRoundStep

            assert.equal(step.kind, TransformationSteps.RoundStepKind, 'has the kind')
          })

          it('drops the as lower case onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.AsLowerCaseStepKind}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as TransformationSteps.IAsLowerCaseStep

            assert.equal(step.kind, TransformationSteps.AsLowerCaseStepKind, 'has the kind')
          })

          it('drops the as upper case onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.AsUpperCaseStepKind}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as TransformationSteps.IAsUpperCaseStep

            assert.equal(step.kind, TransformationSteps.AsUpperCaseStepKind, 'has the kind')
          })

          it('drops the trim onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.TrimStepKind}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as TransformationSteps.ITrimStep

            assert.equal(step.kind, TransformationSteps.TrimStepKind, 'has the kind')
          })

          it('drops the substring onto an assertion field', async () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${TransformationSteps.SubstringStepKind}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 1, 'has added step')
            const step = editor.model[0].steps[0] as TransformationSteps.ISubstringStep

            assert.equal(step.kind, TransformationSteps.SubstringStepKind, 'has the kind')
          })

          it('drops the data source onto a drop target', async () => {
            editor.model = [
              {
                steps: [
                  new DataSteps.ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
                  new CoreAssertions.EqualAssertion().toJSON(),
                ],
              },
            ]
            await editor.updateComplete
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${DataSteps.ReadValueStepKind}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps [data-step-index="1"]`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 3, 'has the added step')
            const step = editor.model[0].steps[1] as DataSteps.IReadValueStep

            assert.equal(step.kind, DataSteps.ReadValueStepKind, 'puts the added step at the position')
          })
        })
      })

      describe('Assertion chips', () => {
        describe('chips rendering', () => {
          let editor: HttpAssertionsElement
          beforeEach(async () => {
            editor = await basicFixture()
            editor.addAssertion()
          })

          it('renders the Equal assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.EqualAssertionKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Equal', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Not equal assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.NotEqualAssertionKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Not equal', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Greater than assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.GreaterThanAssertionKind}"]:not([data-variant])`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Greater than', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Greater than equal assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.GreaterThanAssertionKind}"][data-variant]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.equal(chip.dataset.variant, 'equal', 'has the variant')
            assert.equal(chip.innerText.trim(), 'Greater than equal', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Less than assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.LessThanAssertionKind}"]:not([data-variant])`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Less than', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Less than equal assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.LessThanAssertionKind}"][data-variant]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.equal(chip.dataset.variant, 'equal', 'has the variant')
            assert.equal(chip.innerText.trim(), 'Less than equal', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Includes assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.IncludesAssertionKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Includes', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Not includes assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.NotIncludesAssertionKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Not includes', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Matches assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.MatchesAssertionKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Matches', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the OK assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.OkAssertionKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'OK', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Not OK assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.NotOkAssertionKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Not OK', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the To be... assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.ToBeAssertionKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'To be...', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })

          it('renders the Not to be... assertion', () => {
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.NotToBeAssertionKind}"]`
            ) as UiChip
            assert.ok(chip, 'has the chip')
            assert.equal(chip.dataset.group, 'assertion', 'has the group')
            assert.notOk(chip.dataset.variant, 'has no variant')
            assert.equal(chip.innerText.trim(), 'Not to be...', 'has the label')
            assert.isTrue(chip.draggable, 'the chip is draggable')
          })
        })

        describe('drag and drop', () => {
          let editor: HttpAssertionsElement
          beforeEach(async () => {
            editor = await basicFixture()
            editor.addAssertion()
            await editor.updateComplete
          })

          afterEach(async () => {
            await resetMouse()
          })
          ;[
            CoreAssertions.EqualAssertionKind,
            CoreAssertions.NotEqualAssertionKind,
            CoreAssertions.IncludesAssertionKind,
            CoreAssertions.NotIncludesAssertionKind,
            CoreAssertions.MatchesAssertionKind,
            CoreAssertions.OkAssertionKind,
            CoreAssertions.NotOkAssertionKind,
            CoreAssertions.ToBeAssertionKind,
            CoreAssertions.NotToBeAssertionKind,
          ].forEach((kind) => {
            it(`drops the ${kind} onto an assertion field`, async () => {
              const chip = editor.shadowRoot!.querySelector(`ui-chip[data-kind="${kind}"]`) as UiChip
              const target = editor.shadowRoot!.querySelector(`.param-row .steps`) as HTMLElement
              await UiMock.dragAndDrop(chip, target)
              await editor.updateComplete

              assert.lengthOf(editor.model[0].steps, 1, 'has added step')
              const step = editor.model[0].steps[0] as IHttpStep

              assert.equal(step.kind, kind, 'has the kind')
            })
          })

          it('drops the data source onto a drop target', async () => {
            editor.model = [
              {
                steps: [
                  new DataSteps.ReadDataSourceStep(FlowSourceEnum.response, FlowResponseDataEnum.headers).toJSON(),
                  new CoreAssertions.EqualAssertion().toJSON(),
                ],
              },
            ]
            await editor.updateComplete
            const chip = editor.shadowRoot!.querySelector(
              `ui-chip[data-kind="${CoreAssertions.EqualAssertionKind}"]`
            ) as UiChip
            const target = editor.shadowRoot!.querySelector(`.param-row .steps [data-step-index="1"]`) as HTMLElement
            await UiMock.dragAndDrop(chip, target)
            await editor.updateComplete

            assert.lengthOf(editor.model[0].steps, 3, 'has the added step')
            const step = editor.model[0].steps[1] as CoreAssertions.IEqualAssertion

            assert.equal(step.kind, CoreAssertions.EqualAssertionKind, 'puts the added step at the position')
          })
        })

        describe('updating values', () => {
          let editor: HttpAssertionsElement
          beforeEach(async () => {
            editor = await basicFixture()
            editor.addAssertion()
          })

          it('updates the value of the equal assertion', async () => {
            editor.model = [
              {
                steps: [new CoreAssertions.EqualAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const spy = sinon.spy()
            editor.addEventListener('change', spy)
            const input = editor.shadowRoot!.querySelector(
              `ui-filled-text-field[data-kind="${CoreAssertions.EqualAssertionKind}"]`
            ) as Input
            input.value = 'test value'
            input.dispatchEvent(new Event('change'))
            assert.isTrue(spy.calledOnce, 'dispatched the change event')
            const step = editor.model[0].steps[0] as CoreAssertions.IEqualAssertion
            assert.equal(step.value, 'test value')
          })

          it('updates the value of the not equal assertion', async () => {
            editor.model = [
              {
                steps: [new CoreAssertions.NotEqualAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const spy = sinon.spy()
            editor.addEventListener('change', spy)
            const input = editor.shadowRoot!.querySelector(
              `ui-filled-text-field[data-kind="${CoreAssertions.NotEqualAssertionKind}"]`
            ) as Input
            input.value = 'test value'
            input.dispatchEvent(new Event('change'))
            assert.isTrue(spy.calledOnce, 'dispatched the change event')
            const step = editor.model[0].steps[0] as CoreAssertions.INotEqualAssertion
            assert.equal(step.value, 'test value')
          })

          it('updates the value of the includes assertion', async () => {
            editor.model = [
              {
                steps: [new CoreAssertions.IncludesAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const spy = sinon.spy()
            editor.addEventListener('change', spy)
            const input = editor.shadowRoot!.querySelector(
              `ui-filled-text-field[data-kind="${CoreAssertions.IncludesAssertionKind}"]`
            ) as Input
            input.value = 'test value'
            input.dispatchEvent(new Event('change'))
            assert.isTrue(spy.calledOnce, 'dispatched the change event')
            const step = editor.model[0].steps[0] as CoreAssertions.IIncludesAssertion
            assert.equal(step.value, 'test value')
          })

          it('updates the value of the not includes assertion', async () => {
            editor.model = [
              {
                steps: [new CoreAssertions.NotIncludesAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const spy = sinon.spy()
            editor.addEventListener('change', spy)
            const input = editor.shadowRoot!.querySelector(
              `ui-filled-text-field[data-kind="${CoreAssertions.NotIncludesAssertionKind}"]`
            ) as Input

            input.value = 'test value'
            input.dispatchEvent(new Event('change'))
            assert.isTrue(spy.calledOnce, 'dispatched the change event')
            const step = editor.model[0].steps[0] as CoreAssertions.INotIncludesAssertion
            assert.equal(step.value, 'test value')
          })

          it('updates the value of the matches assertion', async () => {
            editor.model = [
              {
                steps: [new CoreAssertions.MatchesAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const spy = sinon.spy()
            editor.addEventListener('change', spy)
            const input = editor.shadowRoot!.querySelector(
              `ui-filled-text-field[data-kind="${CoreAssertions.MatchesAssertionKind}"]`
            ) as Input

            input.value = 'test value'
            input.dispatchEvent(new Event('change'))
            assert.isTrue(spy.calledOnce, 'dispatched the change event')
            const step = editor.model[0].steps[0] as CoreAssertions.IMatchesAssertion
            assert.equal(step.value, 'test value')
          })

          it('updates the value of the to be assertion', async () => {
            editor.model = [
              {
                steps: [new CoreAssertions.ToBeAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const spy = sinon.spy()
            editor.addEventListener('change', spy)
            const input = editor.shadowRoot!.querySelector(
              `ui-filled-text-field[data-kind="${CoreAssertions.ToBeAssertionKind}"]`
            ) as Input

            input.value = 'test value'
            input.dispatchEvent(new Event('change'))
            assert.isTrue(spy.calledOnce, 'dispatched the change event')
            const step = editor.model[0].steps[0] as CoreAssertions.IToBeAssertion
            assert.equal(step.value, 'test value')
          })

          it('updates the value of the not to be assertion', async () => {
            editor.model = [
              {
                steps: [new CoreAssertions.NotToBeAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const spy = sinon.spy()
            editor.addEventListener('change', spy)
            const input = editor.shadowRoot!.querySelector(
              `ui-filled-text-field[data-kind="${CoreAssertions.NotToBeAssertionKind}"]`
            ) as Input

            input.value = 'test value'
            input.dispatchEvent(new Event('change'))
            assert.isTrue(spy.calledOnce, 'dispatched the change event')
            const step = editor.model[0].steps[0] as CoreAssertions.INotToBeAssertion
            assert.equal(step.value, 'test value')
          })

          it('updates the value of the greater than be assertion', async () => {
            editor.model = [
              {
                steps: [new CoreAssertions.GreaterThanAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const spy = sinon.spy()
            editor.addEventListener('change', spy)
            const input = editor.shadowRoot!.querySelector(
              `ui-filled-text-field[data-kind="${CoreAssertions.GreaterThanAssertionKind}"]`
            ) as Input

            input.value = '20'
            input.dispatchEvent(new Event('change'))
            assert.isTrue(spy.calledOnce, 'dispatched the change event')
            const step = editor.model[0].steps[0] as CoreAssertions.IGreaterThanAssertion
            assert.equal(step.value, 20)
          })

          it('updates the value of the less than be assertion', async () => {
            editor.model = [
              {
                steps: [new CoreAssertions.LessThanAssertion().toJSON()],
              },
            ]
            await editor.updateComplete
            const spy = sinon.spy()
            editor.addEventListener('change', spy)
            const input = editor.shadowRoot!.querySelector(
              `ui-filled-text-field[data-kind="${CoreAssertions.LessThanAssertionKind}"]`
            ) as Input

            input.value = '20'
            input.dispatchEvent(new Event('change'))
            assert.isTrue(spy.calledOnce, 'dispatched the change event')
            const step = editor.model[0].steps[0] as CoreAssertions.ILessThanAssertion
            assert.equal(step.value, 20)
          })
        })
      })
    })
  })
})
