Модульное тестирование таблицы данных Vuetify с помощью Jest - PullRequest
0 голосов
/ 28 апреля 2020

Я создал приложение Vue. js с Vuetify и сейчас пытаюсь выполнить модульное тестирование компонента, содержащего таблицу данных Vuetify. Таблица данных заполняется из внутреннего REST API с помощью Ax ios, который прекрасно работает при запуске приложения, однако в моем модульном тесте (где я макетирую Ax ios с помощью Jest) таблица данных никогда не заполняется

Вот источник моего компонента

<template>
  <v-container fluid>
    <v-card>
      <v-card-title>
        Results
        <v-spacer></v-spacer>
        <v-text-field
          v-model="search"
          append-icon="mdi-magnify"
          label="Search"
          single-line
          hide-details
        ></v-text-field>
      </v-card-title>
      <v-data-table
        :headers="headers"
        :items="results"
        :search="search"
        :loading="loading"
        loading-text="Loading results..."
        :custom-sort="customSort"
      >
        <template v-slot:item.startTime="{item}">{{formatDate(item.startTime)}}</template>
        <template v-slot:item.endTime="{item}">{{formatDate(item.endTime)}}</template>
      </v-data-table>
    </v-card>
  </v-container>
</template>

<script>
import axios from 'axios';
import moment from 'moment';

function dateArrayToMoment(date) {
  return moment()
    .year(date[0])
    .month(date[1])
    .date(date[2])
    .hour(date[3])
    .minute(date[4]);
}

export default {
  name: 'ResultsList',
  data() {
    return {
      loading: true,
      search: '',
      headers: [
        { text: 'Task', align: 'start', sortable: false, value: 'title' },
        { text: 'State', value: 'state' },
        { text: 'Start Time', value: 'startTime' },
        { text: 'End Time', value: 'endTime' },
        { text: 'Result', value: 'resultMessage' }
      ],
      results: []
    };
  },
  mounted() {
    this.loadResults();
  },
  methods: {
    async loadResults() {
      try {
        let response = await axios.get('BACKEND_SERVER/results', {});
        this.results = response.data;
        this.loading = false;
        // eslint-disable-next-line no-debugger
      } catch (error) {
        // eslint-disable-next-line no-console
        console.error(error);
        // you can handle different errors differently
        // or just display an error message instead of your table using a <v-if> tag
        // or navigate to an error page
      }
    },
    formatDate(date) {
      return dateArrayToMoment(date).format('YYYY-MM-DD hh:mm');
    },
    customSort(items, index, isDesc) {
      items.sort((a, b) => {
        if (index[0] == 'startTime' || index[0] == 'endTime') {
          if (!isDesc[0]) {
            return (
              dateArrayToMoment(b[index]).toDate() -
              dateArrayToMoment(a[index]).toDate()
            );
          } else {
            return (
              dateArrayToMoment(a[index]).toDate() -
              dateArrayToMoment(b[index]).toDate()
            );
          }
        }
      });
      return items;
    }
  }
};
</script>

А вот тестовое выражение c, которое тестирует компонент

import Vue from 'vue'
import Vuetify from 'vuetify'
import ResultsList from '@/components/ResultsList'
import { mount, createLocalVue } from '@vue/test-utils'
import axios from 'axios'
import flushPromises from 'flush-promises';

Vue.use(Vuetify)

const localVue = createLocalVue()

var results = [
  {
    'id': 1,
    'state': 'COMPLETED',
    'startTime': [2020, 4, 21, 19, 42],
    'endTime': [2020, 4, 21, 19, 42],
    'type': 'Example Scheduled Task',
    'title': 'Example Scheduled Task at 2020-04-21 19:42:00',
    'resultMessage': 'Task finished successfully'
  },
  {
    'id': 2,
    'state': 'COMPLETED',
    'startTime': [2020, 4, 22, 13, 36],
    'endTime': [2020, 4, 22, 13, 36],
    'type': 'Example Scheduled Task',
    'title': 'Example Scheduled Task at 2020-04-22 13:36:00',
    'resultMessage': 'Task finished successfully'
  },
  {
    'id': 3,
    'state': 'COMPLETED',
    'startTime': [2020, 4, 22, 13, 37],
    'endTime': [2020, 4, 22, 13, 37],
    'type': 'Example Scheduled Task',
    'title': 'Example Scheduled Task at 2020-04-22 13:37:00',
    'resultMessage': 'Task finished successfully'
  }
];

// Use Jest to mock the Axios
jest.mock('axios');

describe('ResultsList.vue', () => {
  let vuetify

  beforeEach(() => {
    vuetify = new Vuetify()

    axios.get.mockResolvedValue(results);
  })

  it('should have a custom title and match snapshot', async () => {
    const wrapper = mount(ResultsList, {
      localVue,
      vuetify,
      propsData: {
        title: 'Foobar',
      },
    })

    await flushPromises()

    // With jest we can create snapshot files of the HTML output
    expect(wrapper.html()).toMatchSnapshot()

  })
})

Как видите, я использую Jest для насмешки Out Ax ios, поэтому он возвращает некоторые тестовые данные и использует Jest для проверки снимка.

Проблема заключается в том, что моментальный снимок не содержит никаких данных (тестовых или иных), несмотря на вызов flushPromises, чтобы убедиться, что все обещания разрешены до создания моментального снимка.

Вот снимок. Как вы можете видеть, данные не отображаются в таблице данных, тесте или иным образом.

// Jest Snapshot v1

exports[`ResultsList.vue should match snapshot 1`] = `
<div class="container container--fluid" title="Foobar">
  <div class="v-card v-sheet theme--light">
    <div class="v-card__title">
      Results
      <div class="spacer"></div>
      <div class="v-input v-input--hide-details theme--light v-text-field v-text-field--single-line">
        <div class="v-input__control">
          <div class="v-input__slot">
            <div class="v-text-field__slot"><label for="input-4" class="v-label theme--light" style="left: 0px; position: absolute;">Search</label><input id="input-4" type="text"></div>
            <div class="v-input__append-inner">
              <div class="v-input__icon v-input__icon--append"><i aria-hidden="true" class="v-icon notranslate mdi mdi-magnify theme--light"></i></div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="v-data-table theme--light">
      <div class="v-data-table__wrapper">
        <table>
          <colgroup>
            <col class="">
            <col class="">
            <col class="">
            <col class="">
            <col class="">
          </colgroup>
          <thead class="v-data-table-header">
            <tr>
              <th role="columnheader" scope="col" aria-label="Task" class="text-start"><span>Task</span></th>
              <th role="columnheader" scope="col" aria-label="State: Not sorted. Activate to sort ascending." aria-sort="none" class="text-start sortable"><span>State</span><i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i></th>
              <th role="columnheader" scope="col" aria-label="Start Time: Not sorted. Activate to sort ascending." aria-sort="none" class="text-start sortable"><span>Start Time</span><i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i></th>
              <th role="columnheader" scope="col" aria-label="End Time: Not sorted. Activate to sort ascending." aria-sort="none" class="text-start sortable"><span>End Time</span><i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i></th>
              <th role="columnheader" scope="col" aria-label="Result: Not sorted. Activate to sort ascending." aria-sort="none" class="text-start sortable"><span>Result</span><i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i></th>
            </tr>
          </thead>
          <tbody>
            <tr class="v-data-table__empty-wrapper">
              <td colspan="5">No data available</td>
            </tr>
          </tbody>
        </table>
      </div>
      <div class="v-data-footer">
        <div class="v-data-footer__select">Rows per page:<div class="v-input v-input--hide-details v-input--is-label-active v-input--is-dirty theme--light v-text-field v-select">
            <div class="v-input__control">
              <div role="button" aria-haspopup="listbox" aria-expanded="false" aria-owns="list-17" class="v-input__slot">
                <div class="v-select__slot">
                  <div class="v-select__selections">
                    <div class="v-select__selection v-select__selection--comma">10</div><input aria-label="$vuetify.dataTable.itemsPerPageText" id="input-17" readonly="readonly" type="text" aria-readonly="false" autocomplete="off">
                  </div>
                  <div class="v-input__append-inner">
                    <div class="v-input__icon v-input__icon--append"><i aria-hidden="true" class="v-icon notranslate mdi mdi-menu-down theme--light"></i></div>
                  </div><input type="hidden" value="10">
                </div>
                <div class="v-menu">
                  <!---->
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="v-data-footer__pagination">–</div>
        <div class="v-data-footer__icons-before"><button type="button" disabled="disabled" class="v-btn v-btn--disabled v-btn--flat v-btn--icon v-btn--round v-btn--text theme--light v-size--default" aria-label="Previous page"><span class="v-btn__content"><i aria-hidden="true" class="v-icon notranslate mdi mdi-chevron-left theme--light"></i></span></button></div>
        <div class="v-data-footer__icons-after"><button type="button" disabled="disabled" class="v-btn v-btn--disabled v-btn--flat v-btn--icon v-btn--round v-btn--text theme--light v-size--default" aria-label="Next page"><span class="v-btn__content"><i aria-hidden="true" class="v-icon notranslate mdi mdi-chevron-right theme--light"></i></span></button></div>
      </div>
    </div>
  </div>
</div>
`;

1 Ответ

1 голос
/ 29 апреля 2020

Некоторые идеи: Позвоните wrapper.vm.$nextTick(), прежде чем сделать снимок.

Вместо использования Vue.use(), попробуйте localVue.use(Vuetify)

...