Skip to content

Nuxt, тестирование и Storybook

Модуль Nuxt

ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['vue-feature-toggles/nuxt'],

  featureToggles: {
    flags: { newDashboard: true, betaSearch: false },
    urlOverrides: true,
    urlPrefix: 'feature',
    defaultValue: false,
    reloadInterval: 0,
    meta: { newDashboard: { owner: 'alice', addedAt: '2025-03-01' } },
    expiry: { betaBanner: '2025-06-01' },
    groups: { beta: ['betaSearch'] },
    dependencies: { aiSuggestions: ['newSearch'] },
    liveUpdates: { type: 'sse', url: '/api/flags/stream' },
  },
})
  • <Feature>, <FeatureVariant> и v-feature регистрируются глобально
  • SSR-гидратация через nuxtApp.payload обрабатывается автоматически — конфигурация ssrState не нужна
  • $featureToggles внедряется в nuxtApp
ts
// plugins/my-plugin.ts
export default defineNuxtPlugin((nuxtApp) => {
  const { $featureToggles } = nuxtApp
  console.log($featureToggles.flags.value)
})

Пользовательский загрузчик с Nuxt

Опция loader — это функция, и она не может быть сериализована в nuxt.config. Вместо этого используйте плагин:

ts
// plugins/feature-toggles.ts
import { FeatureToggles } from 'vue-feature-toggles'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(FeatureToggles, {
    loader: async () => {
      return $fetch('/api/flags')
    },
    urlOverrides: true,
  })
})

Утилиты для тестирования

ts
import {
  createTestFeatureProvider,
  withFeatures,
  setTestFlag,
  resetTestProvider,
} from 'vue-feature-toggles/testing'

Монтирование с флагами

ts
import { mount } from '@vue/test-utils'
import { withFeatures } from 'vue-feature-toggles/testing'

const wrapper = mount(
  MyComponent,
  withFeatures({
    newDashboard: true,
    betaSearch: false,
  }),
)

Полный доступ к провайдеру в тестах

ts
import { createTestFeatureProvider } from 'vue-feature-toggles/testing'

const { install, provider } = createTestFeatureProvider({ newDashboard: true })

const wrapper = mount(MyComponent, {
  global: { plugins: [{ install }] },
})

provider.setFlag('newDashboard', false)
await nextTick()

Изменение флагов посреди теста

ts
import { setTestFlag } from 'vue-feature-toggles/testing'

it('hides the component when flag is off', async () => {
  const wrapper = mount(MyComponent, withFeatures({ betaSearch: true }))
  expect(wrapper.find('[data-testid="beta"]').exists()).toBe(true)

  await setTestFlag('betaSearch', false) // автоматически дожидается nextTick
  expect(wrapper.find('[data-testid="beta"]').exists()).toBe(false)
})

Очистка

ts
import { resetTestProvider } from 'vue-feature-toggles/testing'

afterEach(() => resetTestProvider())

Точка входа vue-feature-toggles/testing исключена из продакшен-бандла.

Аддон Storybook

ts
// .storybook/preview.ts
import { withFeatureToggles } from 'vue-feature-toggles/storybook'

export const decorators = [
  withFeatureToggles({ betaSearch: false }), // глобальные значения по умолчанию
]
ts
// MyComponent.stories.ts
export const WithBetaSearch: Story = {
  parameters: {
    featureToggles: {
      betaSearch: true,
      newDashboard: false,
    },
  },
}

parameters.featureToggles каждой истории объединяется поверх глобальных значений по умолчанию. Точка входа vue-feature-toggles/storybook исключена из продакшен-бандла.