|
1 | 1 | import { HarnessLoader } from '@angular/cdk/testing'; |
2 | 2 | import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; |
3 | | -import { ReactiveFormsModule } from '@angular/forms'; |
| 3 | +import { ReactiveFormsModule, Validators } from '@angular/forms'; |
4 | 4 | import { MatButtonHarness } from '@angular/material/button/testing'; |
5 | 5 | import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator/jest'; |
| 6 | +import { MockStore, provideMockStore } from '@ngrx/store/testing'; |
6 | 7 | import { mockApi, mockCall } from 'app/core/testing/utils/mock-api.utils'; |
7 | 8 | import { mockAuth } from 'app/core/testing/utils/mock-auth.utils'; |
8 | 9 | import { |
9 | 10 | DatasetRecordSize, DatasetSnapdev, DatasetSync, DatasetType, |
10 | 11 | } from 'app/enums/dataset.enum'; |
11 | 12 | import { DeduplicationSetting } from 'app/enums/deduplication-setting.enum'; |
12 | 13 | import { EncryptionKeyFormat } from 'app/enums/encryption-key-format.enum'; |
| 14 | +import { LicenseFeature } from 'app/enums/license-feature.enum'; |
13 | 15 | import { OnOff } from 'app/enums/on-off.enum'; |
| 16 | +import { ProductType } from 'app/enums/product-type.enum'; |
14 | 17 | import { inherit } from 'app/enums/with-inherit.enum'; |
15 | 18 | import { ZfsPropertySource } from 'app/enums/zfs-property-source.enum'; |
16 | 19 | import { Dataset } from 'app/interfaces/dataset.interface'; |
17 | 20 | import { QueryFilter } from 'app/interfaces/query-api.interface'; |
| 21 | +import { SystemInfo } from 'app/interfaces/system-info.interface'; |
18 | 22 | import { DetailsTableHarness } from 'app/modules/details-table/details-table.harness'; |
19 | 23 | import { DialogService } from 'app/modules/dialog/dialog.service'; |
20 | 24 | import { IxFormHarness } from 'app/modules/forms/ix-forms/testing/ix-form.harness'; |
21 | 25 | import { SlideInRef } from 'app/modules/slide-ins/slide-in-ref'; |
22 | 26 | import { ApiService } from 'app/modules/websocket/api.service'; |
23 | 27 | import { ZvolFormComponent } from 'app/pages/datasets/components/zvol-form/zvol-form.component'; |
| 28 | +import { selectIsEnterprise, selectSystemInfo } from 'app/store/system-info/system-info.selectors'; |
24 | 29 |
|
25 | 30 | describe('ZvolFormComponent', () => { |
26 | 31 | let loader: HarnessLoader; |
@@ -135,6 +140,16 @@ describe('ZvolFormComponent', () => { |
135 | 140 | mockProvider(DialogService), |
136 | 141 | mockProvider(SlideInRef, slideInRef), |
137 | 142 | mockAuth(), |
| 143 | + provideMockStore({ |
| 144 | + initialState: { |
| 145 | + systemInfo: { |
| 146 | + productType: ProductType.CommunityEdition, |
| 147 | + systemInfo: { |
| 148 | + license: { features: [] }, |
| 149 | + } as SystemInfo, |
| 150 | + }, |
| 151 | + }, |
| 152 | + }), |
138 | 153 | ], |
139 | 154 | }); |
140 | 155 |
|
@@ -207,6 +222,69 @@ describe('ZvolFormComponent', () => { |
207 | 222 | }); |
208 | 223 | }); |
209 | 224 |
|
| 225 | + describe('deduplication visibility', () => { |
| 226 | + // overrideSelector mutates the module-singleton selectors, so reset them |
| 227 | + // afterwards to avoid leaking the enterprise/license state into other tests. |
| 228 | + afterEach(() => { |
| 229 | + spectator.inject(MockStore).resetSelectors(); |
| 230 | + }); |
| 231 | + |
| 232 | + async function setupVisibilityTest(isEnterprise: boolean, hasDedupLicense: boolean): Promise<void> { |
| 233 | + spectator = createComponent({ |
| 234 | + providers: [ |
| 235 | + mockProvider(SlideInRef, { |
| 236 | + ...slideInRef, |
| 237 | + getData: jest.fn(() => ({ isNew: true, parentOrZvolId: 'parentId' })), |
| 238 | + }), |
| 239 | + ], |
| 240 | + }); |
| 241 | + const store$ = spectator.inject(MockStore); |
| 242 | + store$.overrideSelector(selectIsEnterprise, isEnterprise); |
| 243 | + store$.overrideSelector(selectSystemInfo, { |
| 244 | + license: { |
| 245 | + features: hasDedupLicense ? [LicenseFeature.Dedup] : [], |
| 246 | + }, |
| 247 | + } as SystemInfo); |
| 248 | + store$.refreshState(); |
| 249 | + loader = TestbedHarnessEnvironment.loader(spectator.fixture); |
| 250 | + await spectator.fixture.whenStable(); |
| 251 | + mainDetails = await loader.getHarness(DetailsTableHarness); |
| 252 | + } |
| 253 | + |
| 254 | + it('shows deduplication when not enterprise', async () => { |
| 255 | + await setupVisibilityTest(false, false); |
| 256 | + expect(Object.keys(await mainDetails.getValues())).toContain('ZFS Deduplication'); |
| 257 | + expect(spectator.component.form.controls.deduplication.hasValidator(Validators.required)).toBe(true); |
| 258 | + }); |
| 259 | + |
| 260 | + it('shows deduplication when enterprise with dedup license', async () => { |
| 261 | + await setupVisibilityTest(true, true); |
| 262 | + expect(Object.keys(await mainDetails.getValues())).toContain('ZFS Deduplication'); |
| 263 | + expect(spectator.component.form.controls.deduplication.hasValidator(Validators.required)).toBe(true); |
| 264 | + }); |
| 265 | + |
| 266 | + it('hides deduplication when enterprise without dedup license', async () => { |
| 267 | + await setupVisibilityTest(true, false); |
| 268 | + expect(Object.keys(await mainDetails.getValues())).not.toContain('ZFS Deduplication'); |
| 269 | + // Hidden control drops its required validator so it never blocks submission. |
| 270 | + expect(spectator.component.form.controls.deduplication.hasValidator(Validators.required)).toBe(false); |
| 271 | + }); |
| 272 | + |
| 273 | + it('omits deduplication from the create payload when hidden', async () => { |
| 274 | + await setupVisibilityTest(true, false); |
| 275 | + form = await loader.getHarness(IxFormHarness); |
| 276 | + await form.fillForm({ Name: 'new zvol', Size: '1 GiB' }); |
| 277 | + |
| 278 | + const saveButton = await loader.getHarness(MatButtonHarness.with({ text: 'Save' })); |
| 279 | + await saveButton.click(); |
| 280 | + |
| 281 | + expect(spectator.inject(ApiService).call).toHaveBeenLastCalledWith( |
| 282 | + 'pool.dataset.create', |
| 283 | + [expect.not.objectContaining({ deduplication: expect.anything() })], |
| 284 | + ); |
| 285 | + }); |
| 286 | + }); |
| 287 | + |
210 | 288 | describe('adds a new zvol with encrypted parent', () => { |
211 | 289 | let encryptedLoader: HarnessLoader; |
212 | 290 | let encryptedSpectator: Spectator<ZvolFormComponent>; |
@@ -303,6 +381,7 @@ describe('ZvolFormComponent', () => { |
303 | 381 | }); |
304 | 382 |
|
305 | 383 | loader = TestbedHarnessEnvironment.loader(spectator.fixture); |
| 384 | + await spectator.fixture.whenStable(); |
306 | 385 | form = await loader.getHarness(IxFormHarness); |
307 | 386 | mainDetails = await loader.getHarness(DetailsTableHarness); |
308 | 387 | }); |
|
0 commit comments