mirror of https://github.com/rancher/dashboard.git
361 lines
13 KiB
TypeScript
361 lines
13 KiB
TypeScript
import { mount } from '@vue/test-utils';
|
|
import Labels, { Factory } from '@shell/components/form/Labels.vue';
|
|
import KeyValue from '@shell/components/form/KeyValue.vue';
|
|
import { ToggleSwitch } from '@components/Form/ToggleSwitch';
|
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
|
|
const mockT = jest.fn((key: string) => key);
|
|
|
|
describe('class: Factory', () => {
|
|
const mockProtectedWarning = 'Protected Key';
|
|
|
|
it('should correctly initialize with no protected keys or regexes', () => {
|
|
const factory = new Factory([], [], mockProtectedWarning, { a: '1', b: '2' });
|
|
|
|
expect((factory as any).protectedKeys).toStrictEqual([]);
|
|
expect((factory as any).protectedRegexes).toStrictEqual([]);
|
|
expect((factory as any).protectedWarning).toBe(mockProtectedWarning);
|
|
expect(factory.initValue).toStrictEqual({ a: '1', b: '2' });
|
|
expect(factory.value).toStrictEqual({ a: '1', b: '2' });
|
|
expect(factory.keyErrors).toStrictEqual({});
|
|
expect(factory.hasProtectedKeys).toBe(false);
|
|
});
|
|
|
|
it('should correctly initialize with protected keys', () => {
|
|
const factory = new Factory(['key1'], [], mockProtectedWarning, { key1: 'value1', key2: 'value2' });
|
|
|
|
expect((factory as any).protectedKeys).toStrictEqual(['key1']);
|
|
expect(factory.value).toStrictEqual({ key2: 'value2' });
|
|
expect(factory.hasProtectedKeys).toBe(true);
|
|
});
|
|
|
|
it('should correctly initialize with protected regexes', () => {
|
|
const factory = new Factory([], [/^key/], mockProtectedWarning, { key1: 'value1', other: 'value2' });
|
|
|
|
expect((factory as any).protectedRegexes.map((r: RegExp) => r.source)).toStrictEqual(['^key']);
|
|
expect(factory.value).toStrictEqual({ other: 'value2' });
|
|
expect(factory.hasProtectedKeys).toBe(true);
|
|
});
|
|
|
|
it('should omit protected keys from value but keep them in initValue', () => {
|
|
const factory = new Factory(['protected'], [], mockProtectedWarning, { protected: 'pVal', normal: 'nVal' });
|
|
|
|
expect(factory.initValue).toStrictEqual({ protected: 'pVal', normal: 'nVal' });
|
|
expect(factory.value).toStrictEqual({ normal: 'nVal' });
|
|
});
|
|
|
|
it('should update value and call callbackFn, preserving initial protected keys', () => {
|
|
const initialValue = {
|
|
system: 'sysVal', user: 'userVal', system2: 'sysVal'
|
|
};
|
|
const factory = new Factory(['system', 'system2'], [], mockProtectedWarning, initialValue);
|
|
const callback = jest.fn();
|
|
|
|
const newValue = {
|
|
newUser: 'newUVal', user: 'updatedUserVal', newProtected: 'newPVal', system: 'foo'
|
|
};
|
|
|
|
factory.update(newValue, callback);
|
|
|
|
// The internal factory.value is just the new set
|
|
expect(factory.value).toStrictEqual(newValue);
|
|
|
|
// The callback should receive the mix of new unprotected values and initial protected values
|
|
expect(callback).toHaveBeenCalledWith({
|
|
newProtected: 'newPVal',
|
|
newUser: 'newUVal',
|
|
user: 'updatedUserVal',
|
|
system: 'sysVal', // 'system' is the original protected from initialValue, sysVal is the original value
|
|
system2: 'sysVal' // 'system2' is the original protected from initialValue
|
|
});
|
|
});
|
|
|
|
it('should handle null/undefined update value gracefully', () => {
|
|
const initialValue = { system: 'sysVal', user: 'userVal' };
|
|
const factory = new Factory(['system'], [], mockProtectedWarning, initialValue);
|
|
const callback = jest.fn();
|
|
|
|
factory.update({}, callback);
|
|
|
|
expect(factory.value).toStrictEqual({});
|
|
expect(factory.keyErrors).toStrictEqual({});
|
|
expect(callback).toHaveBeenCalledWith({ system: 'sysVal' }); // Only the original protected values
|
|
});
|
|
|
|
it('should correctly identify protected keys based on both array and regex', () => {
|
|
const factory = new Factory(['staticKey'], [/^dynamic/], mockProtectedWarning, {});
|
|
|
|
expect((factory as any).isProtected('staticKey')).toBe(true);
|
|
expect((factory as any).isProtected('dynamicKey')).toBe(true);
|
|
expect((factory as any).isProtected('otherKey')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('component: Labels', () => {
|
|
const mockValue = {
|
|
labels: {
|
|
'kubernetes.io/hostname': 'test-app',
|
|
'my-label': 'my-value'
|
|
},
|
|
systemLabels: ['kubernetes.io/hostname'],
|
|
annotations: {
|
|
'cattle.io/system': 'user',
|
|
'my-annotation': 'my-annotation'
|
|
},
|
|
systemAnnotations: ['cattle.io/system'],
|
|
setLabels: jest.fn(),
|
|
setAnnotations: jest.fn(),
|
|
};
|
|
|
|
it('should initialize with correct data properties', () => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: 'create',
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
const data = (wrapper.vm as any).$data;
|
|
|
|
expect(data.labels).toBeDefined();
|
|
expect(data.labels.initValue).toStrictEqual(mockValue.labels);
|
|
expect(data.labels.protectedKeys).toStrictEqual(mockValue.systemLabels);
|
|
expect(data.labels.value).toStrictEqual({ 'my-label': 'my-value' });
|
|
expect(data.labels.hasProtectedKeys).toBe(true);
|
|
expect(data.labels.keyErrors).toStrictEqual({}); // only when manually update
|
|
|
|
expect(data.annotations).toBeDefined();
|
|
expect(data.annotations.initValue).toStrictEqual(mockValue.annotations);
|
|
expect(data.annotations.protectedKeys).toStrictEqual(mockValue.systemAnnotations);
|
|
expect(data.annotations.value).toStrictEqual({ 'my-annotation': 'my-annotation' });
|
|
expect(data.annotations.hasProtectedKeys).toBe(true);
|
|
expect(data.annotations.keyErrors).toStrictEqual({}); // only when manually update
|
|
|
|
expect(data.toggler).toBe(false);
|
|
});
|
|
|
|
it('should render KeyValue for labels', () => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: 'create',
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
const labelsKeyValue = wrapper.findComponent(KeyValue);
|
|
|
|
expect(labelsKeyValue.exists()).toBe(true);
|
|
expect(labelsKeyValue.props('value')).toStrictEqual({ 'my-label': 'my-value' });
|
|
expect(labelsKeyValue.props('addLabel')).toBe('labels.addLabel');
|
|
expect(labelsKeyValue.props('mode')).toBe('create');
|
|
expect(labelsKeyValue.props('keyErrors')).toStrictEqual({});
|
|
});
|
|
|
|
it('should render KeyValue for annotations if showAnnotations is true', () => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: 'create',
|
|
showAnnotations: true,
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
const keyValueComponents = wrapper.findAllComponents(KeyValue);
|
|
|
|
expect(keyValueComponents).toHaveLength(2);
|
|
const annotationsKeyValue = keyValueComponents[1]; // The second KeyValue is for annotations
|
|
|
|
expect(annotationsKeyValue.exists()).toBe(true);
|
|
expect(annotationsKeyValue.props('value')).toStrictEqual({ 'my-annotation': 'my-annotation' });
|
|
expect(annotationsKeyValue.props('addLabel')).toBe('labels.addAnnotation');
|
|
expect(annotationsKeyValue.props('mode')).toBe('create');
|
|
expect(annotationsKeyValue.props('keyErrors')).toStrictEqual({});
|
|
});
|
|
|
|
it('should not render KeyValue for annotations if showAnnotations is false', () => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: 'create',
|
|
showAnnotations: false,
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
const keyValueComponents = wrapper.findAllComponents(KeyValue);
|
|
|
|
expect(keyValueComponents).toHaveLength(1); // Only the KeyValue for labels
|
|
});
|
|
|
|
it('should show ToggleSwitch in VIEW mode if there are protected keys', async() => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: _VIEW,
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
expect((wrapper.vm as any).showToggler).toBe(true);
|
|
const toggleSwitch = wrapper.findComponent(ToggleSwitch);
|
|
|
|
expect(toggleSwitch.exists()).toBe(true);
|
|
expect(toggleSwitch.props('onLabel')).toBe('labels.labels.show');
|
|
});
|
|
|
|
it('should not show ToggleSwitch in non-VIEW mode', () => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: _EDIT,
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
expect((wrapper.vm as any).showToggler).toBe(false);
|
|
expect(wrapper.findComponent(ToggleSwitch).exists()).toBe(false);
|
|
});
|
|
|
|
it('should not show ToggleSwitch in VIEW mode if no protected keys', () => {
|
|
const mockValueNoProtected = {
|
|
labels: { 'my-label': 'my-value' },
|
|
systemLabels: [],
|
|
annotations: { 'my-annotation': 'my-annotation' },
|
|
systemAnnotations: [],
|
|
setLabels: jest.fn(),
|
|
setAnnotations: jest.fn(),
|
|
};
|
|
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValueNoProtected,
|
|
mode: _VIEW,
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
expect((wrapper.vm as any).showToggler).toBe(false);
|
|
expect(wrapper.findComponent(ToggleSwitch).exists()).toBe(false);
|
|
});
|
|
|
|
it('should toggle label values when ToggleSwitch is activated', async() => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: _VIEW,
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
const labelsKeyValue = wrapper.findComponent(KeyValue);
|
|
|
|
expect(labelsKeyValue.props('value')).toStrictEqual({ 'my-label': 'my-value' }); // Initial: hidden protected
|
|
|
|
// Activate the toggle
|
|
await wrapper.findComponent(ToggleSwitch).vm.$emit('update:value', true);
|
|
await wrapper.vm.$nextTick();
|
|
|
|
expect((wrapper.vm as any).toggler).toBe(true);
|
|
expect(labelsKeyValue.props('value')).toStrictEqual(mockValue.labels); // Should show all labels including protected
|
|
});
|
|
|
|
it('should toggle annotation values when ToggleSwitch is activated', async() => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: _VIEW,
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
const annotationsKeyValue = wrapper.findAllComponents(KeyValue)[1];
|
|
|
|
expect(annotationsKeyValue.props('value')).toStrictEqual({ 'my-annotation': 'my-annotation' }); // Initial: hidden protected
|
|
|
|
// Activate the toggle
|
|
await wrapper.findComponent(ToggleSwitch).vm.$emit('update:value', true);
|
|
await wrapper.vm.$nextTick();
|
|
|
|
expect((wrapper.vm as any).toggler).toBe(true);
|
|
expect(annotationsKeyValue.props('value')).toStrictEqual(mockValue.annotations); // Should show all annotations including protected
|
|
});
|
|
|
|
it('should call setLabels when labels KeyValue emits update:value', async() => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: 'create',
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
const labelsKeyValue = wrapper.findComponent(KeyValue);
|
|
const newLabels = { 'new-label': 'new-value', 'my-label': 'my-value' };
|
|
|
|
await labelsKeyValue.vm.$emit('update:value', newLabels);
|
|
|
|
// We expect setLabels to be called with the new values, preserving initial protected ones
|
|
expect(mockValue.setLabels).toHaveBeenCalledWith({
|
|
'new-label': 'new-value',
|
|
'my-label': 'my-value',
|
|
'kubernetes.io/hostname': 'test-app'
|
|
});
|
|
});
|
|
|
|
it('should call setAnnotations when annotations KeyValue emits update:value', async() => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: 'create',
|
|
showAnnotations: true,
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
const annotationsKeyValue = wrapper.findAllComponents(KeyValue)[1];
|
|
const newAnnotations = { 'new-anno': 'new-value', 'my-annotation': 'my-annotation' };
|
|
|
|
await annotationsKeyValue.vm.$emit('update:value', newAnnotations);
|
|
|
|
// We expect setAnnotations to be called with the new values, preserving initial protected ones
|
|
expect(mockValue.setAnnotations).toHaveBeenCalledWith({
|
|
'new-anno': 'new-value',
|
|
'my-annotation': 'my-annotation',
|
|
'cattle.io/system': 'user'
|
|
});
|
|
});
|
|
|
|
it('should add newly entered protected keys to keyErrors', async() => {
|
|
const wrapper = mount(Labels, {
|
|
props: {
|
|
value: mockValue,
|
|
mode: 'create',
|
|
},
|
|
global: { mocks: { t: mockT } },
|
|
});
|
|
|
|
// Insert new protected key
|
|
const newLabels = {
|
|
'my-label': 'my-value',
|
|
'kubernetes.io/hostname': 'new-protected-value',
|
|
'non-protected': 'value'
|
|
};
|
|
|
|
await wrapper.vm.labels.update(newLabels, () => {});
|
|
await wrapper.vm.$nextTick();
|
|
|
|
// Expect to add protected labels in keyErrors
|
|
expect((wrapper.vm as any).labels.keyErrors).toStrictEqual({ 'kubernetes.io/hostname': 'labels.protectedWarning' });
|
|
|
|
// On edit, should key the protected label in the list
|
|
expect((wrapper.vm as any).labels.value).toStrictEqual({
|
|
'my-label': 'my-value',
|
|
'kubernetes.io/hostname': 'new-protected-value',
|
|
'non-protected': 'value'
|
|
});
|
|
});
|
|
});
|