diff --git a/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts b/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts index 7433d047252..82e643da46a 100644 --- a/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts @@ -12,14 +12,6 @@ import { } from 'web-test-helpers' import Spaces from '../../../src/views/Spaces.vue' -window.ResizeObserver = - window.ResizeObserver || - jest.fn().mockImplementation(() => ({ - disconnect: jest.fn(), - observe: jest.fn(), - unobserve: jest.fn() - })) - const selectors = { loadingSpinnerStub: 'app-loading-spinner-stub', spacesListStub: 'spaces-list-stub', diff --git a/packages/web-app-files/src/views/spaces/Projects.vue b/packages/web-app-files/src/views/spaces/Projects.vue index 773ef833694..3a284ef5bd1 100644 --- a/packages/web-app-files/src/views/spaces/Projects.vue +++ b/packages/web-app-files/src/views/spaces/Projects.vue @@ -66,7 +66,7 @@ import AppLoadingSpinner from 'web-pkg/src/components/AppLoadingSpinner.vue' import AppBar from '../../components/AppBar/AppBar.vue' import CreateSpace from '../../components/AppBar/CreateSpace.vue' -import { useAccessToken, useStore, useGraphClient } from 'web-pkg/src/composables' +import { useAbility, useAccessToken, useStore, useGraphClient } from 'web-pkg/src/composables' import { loadPreview } from 'web-pkg/src/helpers/preview' import { ImageDimension } from 'web-pkg/src/constants' import SpaceContextActions from '../../components/Spaces/SpaceContextActions.vue' @@ -95,6 +95,7 @@ export default defineComponent({ setup() { const store = useStore() const { selectedResourcesIds } = useSelectedResources({ store }) + const { can } = useAbility() const spaces = computed( () => @@ -118,6 +119,8 @@ export default defineComponent({ return loadResourcesTask.isRunning || !loadResourcesTask.last }) + const hasCreatePermission = computed(() => can('create', 'Space')) + return { ...useSideBar(), ...useScrollTo(), @@ -126,7 +129,8 @@ export default defineComponent({ loadResourcesTask, areResourcesLoading, accessToken, - selectedResourcesIds + selectedResourcesIds, + hasCreatePermission } }, data: function () { @@ -147,9 +151,6 @@ export default defineComponent({ }, showSpaceMemberLabel() { return this.$gettext('Show members') - }, - hasCreatePermission() { - return this.$permissionManager.hasSpaceManagement() } }, watch: { diff --git a/packages/web-app-files/tests/unit/components/AppBar/AppBar.spec.ts b/packages/web-app-files/tests/unit/components/AppBar/AppBar.spec.ts index 7fc12577f42..669a492e280 100644 --- a/packages/web-app-files/tests/unit/components/AppBar/AppBar.spec.ts +++ b/packages/web-app-files/tests/unit/components/AppBar/AppBar.spec.ts @@ -39,14 +39,6 @@ const breadCrumbItemWithContextActionAllowed = { allowContextActions: true } -window.ResizeObserver = - window.ResizeObserver || - jest.fn().mockImplementation(() => ({ - disconnect: jest.fn(), - observe: jest.fn(), - unobserve: jest.fn() - })) - describe('AppBar component', () => { describe('renders', () => { it('by default no breadcrumbs, no bulkactions, no sharesnavigation but viewoptions and sidebartoggle', () => { diff --git a/packages/web-app-files/tests/unit/components/Spaces/SpaceHeader.spec.ts b/packages/web-app-files/tests/unit/components/Spaces/SpaceHeader.spec.ts index 189823ba871..e11d20f4924 100644 --- a/packages/web-app-files/tests/unit/components/Spaces/SpaceHeader.spec.ts +++ b/packages/web-app-files/tests/unit/components/Spaces/SpaceHeader.spec.ts @@ -8,16 +8,6 @@ import { defaultComponentMocks } from 'web-test-helpers' -window.ResizeObserver = - window.ResizeObserver || - jest.fn().mockImplementation(() => ({ - disconnect: jest.fn(), - observe: jest.fn(), - unobserve: jest.fn() - })) - -afterEach(() => jest.clearAllMocks()) - describe('SpaceHeader', () => { it('should add the "squashed"-class when the sidebar is opened', () => { const wrapper = getWrapper({ space: buildSpace({ id: 1 }), sideBarOpen: true }) diff --git a/packages/web-app-files/tests/unit/views/spaces/Projects.spec.ts b/packages/web-app-files/tests/unit/views/spaces/Projects.spec.ts index 986cfd6c505..d5242d20446 100644 --- a/packages/web-app-files/tests/unit/views/spaces/Projects.spec.ts +++ b/packages/web-app-files/tests/unit/views/spaces/Projects.spec.ts @@ -66,9 +66,16 @@ describe('Projects view', () => { expect(wrapper.findAll('.oc-tiles-item').length).toEqual(spaces.length) }) }) + it('should display the "Create Space"-button when permission given', () => { + const { wrapper } = getMountedWrapper({ + abilities: [{ action: 'create', subject: 'Space' }], + stubAppBar: false + }) + expect(wrapper.find('create-space-stub').exists()).toBeTruthy() + }) }) -function getMountedWrapper({ mocks = {}, spaces = [] } = {}) { +function getMountedWrapper({ mocks = {}, spaces = [], abilities = [], stubAppBar = true } = {}) { const defaultMocks = { ...defaultComponentMocks({ currentRoute: mock({ name: 'files-spaces-projects' }) @@ -83,9 +90,14 @@ function getMountedWrapper({ mocks = {}, spaces = [] } = {}) { storeOptions, wrapper: mount(Projects, { global: { - plugins: [...defaultPlugins(), store], + plugins: [...defaultPlugins({ abilities }), store], mocks: defaultMocks, - stubs: { ...defaultStubs, 'space-context-actions': true } + stubs: { + ...defaultStubs, + 'space-context-actions': true, + 'app-bar': stubAppBar, + CreateSpace: true + } } }) } diff --git a/packages/web-app-search/tests/unit/portals/SearchBar.spec.ts b/packages/web-app-search/tests/unit/portals/SearchBar.spec.ts index 859055d7007..17b2bdf13fe 100644 --- a/packages/web-app-search/tests/unit/portals/SearchBar.spec.ts +++ b/packages/web-app-search/tests/unit/portals/SearchBar.spec.ts @@ -51,14 +51,6 @@ const selectors = { jest.mock('lodash-es/debounce', () => (fn) => fn) beforeEach(() => { - jest.resetAllMocks() - - window.ResizeObserver = jest.fn().mockImplementation(() => ({ - disconnect: jest.fn(), - observe: jest.fn(), - unobserve: jest.fn() - })) - providerFiles.previewSearch.search.mockImplementation(() => { return { values: [ diff --git a/packages/web-pkg/package.json b/packages/web-pkg/package.json index 25a0c2635ba..5696c68d40a 100644 --- a/packages/web-pkg/package.json +++ b/packages/web-pkg/package.json @@ -13,6 +13,8 @@ "directory": "packages/web-pkg" }, "peerDependencies": { + "@casl/ability": "^6.3.3", + "@casl/vue": "^2.2.1", "axios": "^0.27.2", "filesize": "^9.0.11", "fuse.js": "^6.5.3", diff --git a/packages/web-pkg/src/composables/ability/index.ts b/packages/web-pkg/src/composables/ability/index.ts new file mode 100644 index 00000000000..a02c4c472f3 --- /dev/null +++ b/packages/web-pkg/src/composables/ability/index.ts @@ -0,0 +1 @@ +export * from './useAbility' diff --git a/packages/web-pkg/src/composables/ability/useAbility.ts b/packages/web-pkg/src/composables/ability/useAbility.ts new file mode 100644 index 00000000000..227538eddcb --- /dev/null +++ b/packages/web-pkg/src/composables/ability/useAbility.ts @@ -0,0 +1,4 @@ +import { useAbility as _useAbility } from '@casl/vue' +import { Ability } from './../../utils' + +export const useAbility = () => _useAbility() diff --git a/packages/web-pkg/src/composables/index.ts b/packages/web-pkg/src/composables/index.ts index eff08b0ff67..bd409613825 100644 --- a/packages/web-pkg/src/composables/index.ts +++ b/packages/web-pkg/src/composables/index.ts @@ -1,3 +1,4 @@ +export * from './ability' export * from './appDefaults' export * from './authContext' export * from './capability' diff --git a/packages/web-pkg/src/utils/types.ts b/packages/web-pkg/src/utils/types.ts index 0b0a61e94ec..ccc9e4f7410 100644 --- a/packages/web-pkg/src/utils/types.ts +++ b/packages/web-pkg/src/utils/types.ts @@ -1,5 +1,11 @@ import { Ref } from 'vue' +import { MongoAbility } from '@casl/ability' export type ReadOnlyRef = Readonly> export type MaybeRef = T | Ref export type MaybeReadonlyRef = MaybeRef | ReadOnlyRef + +export type Actions = 'create' | 'read' | 'update' | 'delete' | 'manage' +export type Subjects = 'Space' + +export type Ability = MongoAbility<[Actions, Subjects]> diff --git a/packages/web-runtime/package.json b/packages/web-runtime/package.json index f42012fb077..955c17b223c 100644 --- a/packages/web-runtime/package.json +++ b/packages/web-runtime/package.json @@ -5,6 +5,8 @@ "description": "ownCloud web runtime", "license": "AGPL-3.0", "dependencies": { + "@casl/ability": "^6.3.3", + "@casl/vue": "^2.2.1", "@fortawesome/fontawesome-free": "6.2.1", "@ownclouders/design-system": "workspace:*", "@popperjs/core": "^2.11.5", diff --git a/packages/web-runtime/src/container/bootstrap.ts b/packages/web-runtime/src/container/bootstrap.ts index 57069b20fd2..ea73b7a5384 100644 --- a/packages/web-runtime/src/container/bootstrap.ts +++ b/packages/web-runtime/src/container/bootstrap.ts @@ -319,7 +319,8 @@ export const announceAuthService = ({ store: Store router: Router }): void => { - authService.initialize(configurationManager, clientService, store, router) + const $ability = app.config.globalProperties.$ability + authService.initialize(configurationManager, clientService, store, router, $ability) app.config.globalProperties.$authService = authService } diff --git a/packages/web-runtime/src/index.ts b/packages/web-runtime/src/index.ts index eb894223907..44bd5ec7c64 100644 --- a/packages/web-runtime/src/index.ts +++ b/packages/web-runtime/src/index.ts @@ -2,6 +2,9 @@ import { DesignSystem as designSystem, pages, translations, supportedLanguages } import { router } from './router' import { configurationManager } from 'web-pkg/src/configuration' import { createHead } from '@vueuse/head' +import { abilitiesPlugin } from '@casl/vue' +import { createMongoAbility } from '@casl/ability' + import { announceConfiguration, initializeApplications, @@ -46,6 +49,7 @@ export const bootstrapApp = async (configurationPath: string): Promise => const store = await announceStore({ runtimeConfiguration }) announcePermissionManager({ app, store }) + app.use(abilitiesPlugin, createMongoAbility([]), { useGlobalProperties: true }) const applicationsPromise = await initializeApplications({ runtimeConfiguration, diff --git a/packages/web-runtime/src/services/auth/authService.ts b/packages/web-runtime/src/services/auth/authService.ts index 708a8984b81..640491571f9 100644 --- a/packages/web-runtime/src/services/auth/authService.ts +++ b/packages/web-runtime/src/services/auth/authService.ts @@ -6,6 +6,7 @@ import { ConfigurationManager } from 'web-pkg/src/configuration' import { RouteLocation, Router } from 'vue-router' import { extractPublicLinkToken, isPublicLinkContext, isUserContext } from '../../router' import { unref } from 'vue' +import { Ability } from 'web-pkg/src/utils' export class AuthService { private clientService: ClientService @@ -14,6 +15,7 @@ export class AuthService { private router: Router private userManager: UserManager private publicLinkManager: PublicLinkManager + private $ability: Ability public hasAuthErrorOccured: boolean @@ -21,13 +23,15 @@ export class AuthService { configurationManager: ConfigurationManager, clientService: ClientService, store: Store, - router: Router + router: Router, + $ability: Ability ): void { this.configurationManager = configurationManager this.clientService = clientService this.store = store this.router = router this.hasAuthErrorOccured = false + this.$ability = $ability } /** @@ -61,7 +65,8 @@ export class AuthService { this.userManager = new UserManager({ clientService: this.clientService, configurationManager: this.configurationManager, - store: this.store + store: this.store, + $ability: this.$ability }) this.userManager.events.addAccessTokenExpired((...args): void => { diff --git a/packages/web-runtime/src/services/auth/userManager.ts b/packages/web-runtime/src/services/auth/userManager.ts index 825dab6ff1f..54916041e75 100644 --- a/packages/web-runtime/src/services/auth/userManager.ts +++ b/packages/web-runtime/src/services/auth/userManager.ts @@ -11,6 +11,8 @@ import { Store } from 'vuex' import isEmpty from 'lodash-es/isEmpty' import axios from 'axios' import { v4 as uuidV4 } from 'uuid' +import { Ability, Actions, Subjects } from 'web-pkg/src/utils' +import { SubjectRawRule } from '@casl/ability' const postLoginRedirectUrlKey = 'oc.postLoginRedirectUrl' type UnloadReason = 'authError' | 'logout' @@ -19,6 +21,7 @@ export interface UserManagerOptions { clientService: ClientService configurationManager: ConfigurationManager store: Store + $ability: Ability } export class UserManager extends OidcUserManager { @@ -28,6 +31,7 @@ export class UserManager extends OidcUserManager { private store: Store private updateAccessTokenPromise: Promise | null private _unloadReason: UnloadReason + private $ability: Ability constructor(options: UserManagerOptions) { const storePrefix = 'oc_oAuth.' @@ -84,6 +88,7 @@ export class UserManager extends OidcUserManager { this.clientService = options.clientService this.configurationManager = options.configurationManager this.store = options.store + this.$ability = options.$ability } /** @@ -132,6 +137,7 @@ export class UserManager extends OidcUserManager { if (!userKnown) { await this.fetchUserInfo(accessToken) + this.updateUserAbilities(this.store.getters.user) this.store.commit('runtime/auth/SET_USER_CONTEXT_READY', true) } })() @@ -266,4 +272,15 @@ export class UserManager extends OidcUserManager { this.store.commit('SET_CAPABILITIES', capabilities) } + + private updateUserAbilities(user) { + const rules: SubjectRawRule[] = [] + + // TODO: expand capabilities + if (!!user.role?.settings.find((s) => s.name === 'create-space')) { + rules.push({ action: 'create', subject: 'Space' }) + } + + this.$ability.update(rules) + } } diff --git a/packages/web-test-helpers/package.json b/packages/web-test-helpers/package.json index 764b8aadc04..f2a4683b522 100644 --- a/packages/web-test-helpers/package.json +++ b/packages/web-test-helpers/package.json @@ -3,6 +3,8 @@ "private": true, "main": "src/index.ts", "peerDependencies": { + "@casl/ability": "^6.3.3", + "@casl/vue": "^2.2.1", "axios": "0.27.2", "vue3-gettext": "^2.3.3", "vue-router": "4.1.6", diff --git a/packages/web-test-helpers/src/defaultPlugins.ts b/packages/web-test-helpers/src/defaultPlugins.ts index 6d3db918a27..4a7bba9191f 100644 --- a/packages/web-test-helpers/src/defaultPlugins.ts +++ b/packages/web-test-helpers/src/defaultPlugins.ts @@ -1,14 +1,19 @@ import DesignSystem from '@ownclouders/design-system' import { createGettext } from 'vue3-gettext' import { h } from 'vue' +import { abilitiesPlugin } from '@casl/vue' +import { createMongoAbility } from '@casl/ability' + export interface DefaultPluginsOptions { designSystem?: boolean gettext?: boolean + abilities?: any } export const defaultPlugins = ({ designSystem = true, - gettext = true + gettext = true, + abilities = [] }: DefaultPluginsOptions = {}) => { const plugins = [] @@ -29,6 +34,12 @@ export const defaultPlugins = ({ }) } + plugins.push({ + install(app) { + app.use(abilitiesPlugin, createMongoAbility(abilities)) + } + }) + plugins.push({ install(app) { app.component('RouterLink', { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4bc6b60385..c6d4ca13ef0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,7 +107,7 @@ importers: caf: 13.1.1 keycode: 2.2.1_nmisee7m676ph5sqah5fka5ueu portal-vue: 2.1.7_vue@3.2.45 - rollup-plugin-gzip: 3.1.0_rollup@3.9.1 + rollup-plugin-gzip: 3.1.0_rollup@3.14.0 uuid: 9.0.0 vite-plugin-static-copy: 0.12.0_vite@4.0.4 vite-plugin-treat-umd-as-commonjs: 0.1.3_vite@4.0.4 @@ -129,7 +129,7 @@ importers: '@ownclouders/prettier-config': link:packages/prettier-config '@ownclouders/tsconfig': link:packages/tsconfig '@playwright/test': 1.29.1 - '@rollup/plugin-inject': 5.0.3_rollup@3.9.1 + '@rollup/plugin-inject': 5.0.3_rollup@3.14.0 '@types/jest': 27.5.2 '@types/jest-axe': 3.5.5 '@types/lodash-es': 4.17.6 @@ -171,7 +171,7 @@ importers: react: 17.0.2 requirejs: 2.3.6 rollup-plugin-node-polyfills: 0.2.1 - rollup-plugin-visualizer: 5.9.0_rollup@3.9.1 + rollup-plugin-visualizer: 5.9.0_rollup@3.14.0 ts-jest: 29.0.3_lpdtk6tcorkuyzgotacsdxtnxm ts-node: 10.9.1_qacynw5b4krdzzrloasmebsynu tslib: 2.4.1 @@ -284,7 +284,7 @@ importers: '@babel/preset-env': 7.19.4_@babel+core@7.19.6 '@babel/runtime': 7.20.1 '@popperjs/core': 2.11.5 - autoprefixer: 10.4.12_postcss@8.4.20 + autoprefixer: 10.4.12_postcss@8.4.21 babel-core: 7.0.0-bridge.0_@babel+core@7.19.6 babel-jest: 29.2.2_@babel+core@7.19.6 babel-loader: 9.1.0_q4ydpsrmbqywduja5orgah6fgq @@ -333,7 +333,7 @@ importers: style-loader: 2.0.0_webpack@4.46.0 style-value-types: 5.1.2 stylelint: 14.16.0 - stylelint-config-sass-guidelines: 9.0.1_l3s4qeu3gkar2knsdy3w5miimm + stylelint-config-sass-guidelines: 9.0.1_4hlw5ipnhspjrftdr7hqo6kmye stylelint-config-standard: 26.0.0_stylelint@14.16.0 tinycolor2: 1.4.2 tippy.js: 6.3.7 @@ -559,7 +559,7 @@ importers: devDependencies: rollup: 3.9.1 rollup-plugin-serve: 1.1.0 - rollup-plugin-vue: 5.1.9_f5iv5tdq5eefnlpdsfwn2yhws4 + rollup-plugin-vue: 5.1.9_msb4omfpi5xaaav7gpce3lwjli vue: 2.7.14 packages/web-app-text-editor: @@ -590,6 +590,8 @@ importers: packages/web-pkg: specifiers: + '@casl/ability': ^6.3.3 + '@casl/vue': ^2.2.1 axios: ^0.27.2 filesize: ^9.0.11 fuse.js: ^6.5.3 @@ -606,6 +608,8 @@ importers: web-client: workspace:@ownclouders/web-client@* web-pkg: workspace:@ownclouders/web-pkg@* dependencies: + '@casl/ability': 6.3.3 + '@casl/vue': 2.2.1_iguwcvvchwz4fu5vynmua7ad3i axios: 0.27.2 filesize: 9.0.11 fuse.js: 6.5.3 @@ -624,6 +628,8 @@ importers: packages/web-runtime: specifiers: + '@casl/ability': ^6.3.3 + '@casl/vue': ^2.2.1 '@fortawesome/fontawesome-free': 6.2.1 '@jest/globals': 29.3.1 '@ownclouders/design-system': workspace:* @@ -674,6 +680,8 @@ importers: webfontloader: ^1.6.28 xml-js: ^1.6.11 dependencies: + '@casl/ability': 6.3.3 + '@casl/vue': 2.2.1_iguwcvvchwz4fu5vynmua7ad3i '@fortawesome/fontawesome-free': 6.2.1 '@ownclouders/design-system': link:../design-system '@popperjs/core': 2.11.5 @@ -727,11 +735,15 @@ importers: packages/web-test-helpers: specifiers: + '@casl/ability': ^6.3.3 + '@casl/vue': ^2.2.1 axios: 0.27.2 vue-router: 4.1.6 vue3-gettext: ^2.3.3 vuex: 4.1.0 dependencies: + '@casl/ability': 6.3.3 + '@casl/vue': 2.2.1_iguwcvvchwz4fu5vynmua7ad3i axios: 0.27.2 vue-router: 4.1.6_vue@3.2.45 vue3-gettext: 2.3.4_tfvhctuaqmmqnirfl65c47tqwu @@ -4218,6 +4230,22 @@ packages: resolution: {integrity: sha512-iZf+UWfL+DogJVpd/xMQyP6X6McYd6ArdYoPMiv/zlOTzeXXfQbYxBNJJBF6tThvsjLMbA8tLjkCdm9RWMFCCw==} dev: true + /@casl/ability/6.3.3: + resolution: {integrity: sha512-UzbqsE9etu6QzZrRmqIyVun2kztAzJ46Tz7lC/2P2buCE6B6Ll7Vptz7JTQtGwapLbeKo2jS7dL966TVOQ7x4g==} + dependencies: + '@ucast/mongo2js': 1.3.3 + dev: false + + /@casl/vue/2.2.1_iguwcvvchwz4fu5vynmua7ad3i: + resolution: {integrity: sha512-1OeGhT4A7VBkEACacF2ZlHkPiFJvyFy9h2PhBnMoetFDojMHbrn3ZjKgL5zQ4wSIrTQo9KbbzG3f0uAei2GKCQ==} + peerDependencies: + '@casl/ability': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.1.0 || ^6.0.0 + vue: ^3.0.0 + dependencies: + '@casl/ability': 6.3.3 + vue: 3.2.45 + dev: false + /@cspotcode/source-map-support/0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -5622,7 +5650,7 @@ packages: resolution: {integrity: sha512-NMrDy6EWh9TPdSRiHmHH2ye1v5U0gBD7pRYwSwJvomx7Bm4GG04vu63dYiVzebLOx2obPpJugew06xVP0Nk7hA==} dev: true - /@rollup/plugin-inject/5.0.3_rollup@3.9.1: + /@rollup/plugin-inject/5.0.3_rollup@3.14.0: resolution: {integrity: sha512-411QlbL+z2yXpRWFXSmw/teQRMkXcAAC8aYTemc15gwJRpvEVDQwoe+N/HTFD8RFG8+88Bme9DK2V9CVm7hJdA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -5631,10 +5659,10 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.2_rollup@3.9.1 + '@rollup/pluginutils': 5.0.2_rollup@3.14.0 estree-walker: 2.0.2 magic-string: 0.27.0 - rollup: 3.9.1 + rollup: 3.14.0 dev: true /@rollup/pluginutils/4.2.1: @@ -5645,7 +5673,7 @@ packages: picomatch: 2.3.1 dev: false - /@rollup/pluginutils/5.0.2_rollup@3.9.1: + /@rollup/pluginutils/5.0.2_rollup@3.14.0: resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -5657,7 +5685,7 @@ packages: '@types/estree': 1.0.0 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 3.9.1 + rollup: 3.14.0 dev: true /@sentry/browser/7.31.1: @@ -6177,6 +6205,30 @@ packages: eslint-visitor-keys: 3.3.0 dev: false + /@ucast/core/1.10.1: + resolution: {integrity: sha512-sXKbvQiagjFh2JCpaHUa64P4UdJbOxYeC5xiZFn8y6iYdb0WkismduE+RmiJrIjw/eLDYmIEXiQeIYYowmkcAw==} + dev: false + + /@ucast/js/3.0.2: + resolution: {integrity: sha512-zxNkdIPVvqJjHI7D/iK8Aai1+59yqU+N7bpHFodVmiTN7ukeNiGGpNmmSjQgsUw7eNcEBnPrZHNzp5UBxwmaPw==} + dependencies: + '@ucast/core': 1.10.1 + dev: false + + /@ucast/mongo/2.4.2: + resolution: {integrity: sha512-/zH1TdBJlYGKKD+Wh0oyD+aBvDSWrwHcD8b4tUL9UgHLhzHtkEnMVFuxbw3SRIRsAa01wmy06+LWt+WoZdj1Bw==} + dependencies: + '@ucast/core': 1.10.1 + dev: false + + /@ucast/mongo2js/1.3.3: + resolution: {integrity: sha512-sBPtMUYg+hRnYeVYKL+ATm8FaRPdlU9PijMhGYKgsPGjV9J4Ks41ytIjGayvKUnBOEhiCaKUUnY4qPeifdqATw==} + dependencies: + '@ucast/core': 1.10.1 + '@ucast/js': 3.0.2 + '@ucast/mongo': 2.4.2 + dev: false + /@unhead/dom/1.0.15: resolution: {integrity: sha512-W3P9eGazfQPMZTG4ryb5oOA02Z4o16Jxo8DAihF/7Xmg/FVYY5Up9p9et7Nbb6AKNgt1PEz3Sp0xBaw+F6Uyjw==} dependencies: @@ -6612,7 +6664,7 @@ packages: - whiskers dev: true - /@vue/component-compiler/4.2.4_f5iv5tdq5eefnlpdsfwn2yhws4: + /@vue/component-compiler/4.2.4_msb4omfpi5xaaav7gpce3lwjli: resolution: {integrity: sha512-tFGw3h3+nxiqnyborwWQ+rUgKAwSFl0Sdg+BCZkWTyFfkEF5fqunTNoklEUDdtRQMmVqsajn1pOZdm0zh4Uicw==} peerDependencies: postcss: '>=6.0' @@ -6621,7 +6673,7 @@ packages: '@vue/component-compiler-utils': 3.2.2_pug@3.0.2 clean-css: 4.2.3 hash-sum: 1.0.2 - postcss: 8.4.20 + postcss: 8.4.21 postcss-modules-sync: 1.0.0 source-map: 0.6.1 vue-template-compiler: 2.7.14 @@ -7556,7 +7608,7 @@ packages: engines: {node: '>=8.0.0'} dev: true - /autoprefixer/10.4.12_postcss@8.4.20: + /autoprefixer/10.4.12_postcss@8.4.21: resolution: {integrity: sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -7568,7 +7620,7 @@ packages: fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.20 + postcss: 8.4.21 postcss-value-parser: 4.2.0 dev: true @@ -18454,13 +18506,13 @@ packages: postcss: 8.4.20 dev: true - /postcss-scss/4.0.6_postcss@8.4.20: + /postcss-scss/4.0.6_postcss@8.4.21: resolution: {integrity: sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.4.19 dependencies: - postcss: 8.4.20 + postcss: 8.4.21 dev: true /postcss-selector-parser/6.0.10: @@ -18575,7 +18627,6 @@ packages: nanoid: 3.3.4 picocolors: 1.0.0 source-map-js: 1.0.2 - dev: false /preact/10.7.1: resolution: {integrity: sha512-MufnRFz39aIhs9AMFisonjzTud1PK1bY+jcJLo6m2T9Uh8AqjD77w11eAAawmjUogoGOnipECq7e/1RClIKsxg==} @@ -19810,13 +19861,13 @@ packages: inherits: 2.0.4 dev: true - /rollup-plugin-gzip/3.1.0_rollup@3.9.1: + /rollup-plugin-gzip/3.1.0_rollup@3.14.0: resolution: {integrity: sha512-PFS9s6/w6dCra6/Z8PGD+ug3aaaqKLDCbr5y1Ek71Wd4rQSmMnOqCIIMgwbYxZ9A/gjP3pCN6rA4pAG47jxF0w==} engines: {node: '>=10.0.0'} peerDependencies: rollup: '>=2.0.0' dependencies: - rollup: 3.9.1 + rollup: 3.14.0 dev: false /rollup-plugin-inject/3.0.2: @@ -19846,7 +19897,7 @@ packages: opener: 1.5.2 dev: false - /rollup-plugin-visualizer/5.9.0_rollup@3.9.1: + /rollup-plugin-visualizer/5.9.0_rollup@3.14.0: resolution: {integrity: sha512-bbDOv47+Bw4C/cgs0czZqfm8L82xOZssk4ayZjG40y9zbXclNk7YikrZTDao6p7+HDiGxrN0b65SgZiVm9k1Cg==} engines: {node: '>=14'} hasBin: true @@ -19858,17 +19909,17 @@ packages: dependencies: open: 8.4.0 picomatch: 2.3.1 - rollup: 3.9.1 + rollup: 3.14.0 source-map: 0.7.4 yargs: 17.6.0 dev: true - /rollup-plugin-vue/5.1.9_f5iv5tdq5eefnlpdsfwn2yhws4: + /rollup-plugin-vue/5.1.9_msb4omfpi5xaaav7gpce3lwjli: resolution: {integrity: sha512-DXzrBUD2j68Y6nls4MmuJsFL1SrQDpdgjxvhk/oy04LzJmXJoX1x31yLEBFkkmvpbon6Q885WJLvEMiMyT+3rA==} peerDependencies: vue-template-compiler: '*' dependencies: - '@vue/component-compiler': 4.2.4_f5iv5tdq5eefnlpdsfwn2yhws4 + '@vue/component-compiler': 4.2.4_msb4omfpi5xaaav7gpce3lwjli '@vue/component-compiler-utils': 3.2.2 debug: 4.3.4 hash-sum: 1.0.2 @@ -19947,7 +19998,6 @@ packages: hasBin: true optionalDependencies: fsevents: 2.3.2 - dev: false /rollup/3.9.1: resolution: {integrity: sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==} @@ -21021,15 +21071,15 @@ packages: stylelint: 14.16.0 dev: true - /stylelint-config-sass-guidelines/9.0.1_l3s4qeu3gkar2knsdy3w5miimm: + /stylelint-config-sass-guidelines/9.0.1_4hlw5ipnhspjrftdr7hqo6kmye: resolution: {integrity: sha512-N06PsVsrgKijQ3YT5hqKA7x3NUkgELTRI1cbWMqcYiCGG6MjzvNk6Cb5YYA1PrvrksBV76BvY9P9bAswojVMqA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: postcss: ^8.3.3 stylelint: ^14.0.1 dependencies: - postcss: 8.4.20 - postcss-scss: 4.0.6_postcss@8.4.20 + postcss: 8.4.21 + postcss-scss: 4.0.6_postcss@8.4.21 stylelint: 14.16.0 stylelint-order: 5.0.0_stylelint@14.16.0 stylelint-scss: 4.3.0_stylelint@14.16.0 diff --git a/tests/unit/config/jest.init.ts b/tests/unit/config/jest.init.ts index 0a5911d6537..eb236a16280 100644 --- a/tests/unit/config/jest.init.ts +++ b/tests/unit/config/jest.init.ts @@ -4,4 +4,11 @@ import fetchMock from 'jest-fetch-mock' observe: jest.fn(), unobserve: jest.fn() })) +;(window as any).ResizeObserver = + (window as any).ResizeObserver || + jest.fn().mockImplementation(() => ({ + disconnect: jest.fn(), + observe: jest.fn(), + unobserve: jest.fn() + })) fetchMock.enableMocks()