import { mount } from 'enzyme';
import React from 'react';
import type { Option } from 'react-select';
import Select, { Creatable } from 'react-select';
import {
AccountSelectInput,
AccountService,
createFakeReactSyntheticEvent,
noop,
ScopeClusterSelector,
} from '@spinnaker/core';
import type { IManifestLabelSelector } from './IManifestLabelSelector';
import { SelectorMode } from './IManifestSelector';
import { ManifestKindSearchService } from '../ManifestKindSearch';
import type { IManifestSelectorState } from './ManifestSelector';
import { ManifestSelector } from './ManifestSelector';
import LabelEditor from './labelEditor/LabelEditor';
import Spy = jasmine.Spy;
describe('', () => {
let searchService: Spy;
beforeEach(() => {
searchService = spyOn(ManifestKindSearchService, 'search').and.returnValue(Promise.resolve([]));
spyOn(AccountService, 'getAllAccountDetailsForProvider').and.returnValue(Promise.resolve([]));
});
describe('initialization', () => {
it('renders namespace from input props', () => {
const wrapper = component({
manifestName: 'configMap my-config-map',
account: 'my-account',
location: 'default',
});
const namespace = wrapper.find({ label: 'Namespace' }).find(Creatable).first();
expect((namespace.props().value as Option).value).toEqual('default');
});
it('renders kind from input props', () => {
const wrapper = component({
manifestName: 'configMap my-config-map',
account: 'my-account',
location: 'default',
});
const kind = wrapper.find({ label: 'Kind' }).find(Creatable).first();
expect((kind.props().value as Option).value).toEqual('configMap');
});
it('renders name from input props', () => {
const wrapper = component({
manifestName: 'configMap my-config-map',
account: 'my-account',
location: 'default',
});
const name = wrapper.find({ label: 'Name' }).find(Creatable).first();
expect((name.props().value as Option).value).toEqual('my-config-map');
});
it('renders kinds from input props', () => {
const wrapper = component({
account: 'my-account',
kinds: ['configMap', 'deployment'],
location: 'default',
mode: SelectorMode.Label,
});
const kinds = wrapper.find({ label: 'Kinds' }).find(Select).first();
expect(kinds.props().value).toEqual(['configMap', 'deployment']);
});
it('renders labels from input props', () => {
const labelSelectors: IManifestLabelSelector[] = [
{
key: 'label-key',
kind: 'EQUALS',
values: ['label-value'],
},
];
const wrapper = component({
account: 'my-account',
kinds: ['configMap', 'deployment'],
labelSelectors: {
selectors: labelSelectors,
},
location: 'default',
mode: SelectorMode.Label,
});
const labelEditor = wrapper.find(LabelEditor);
expect(labelEditor.prop('labelSelectors')).toEqual(labelSelectors);
});
describe('cluster dropdown', () => {
const buildPropsWithApplicationData = (data: any[]) => ({
modes: [SelectorMode.Static, SelectorMode.Dynamic],
application: { getDataSource: () => ({ data }) },
});
it("includes cluster if selected kind matches the cluster's server groups' kind", () => {
const wrapper = component(
{
kind: 'replicaSet',
account: 'my-account',
location: 'default',
mode: SelectorMode.Dynamic,
},
buildPropsWithApplicationData([
{
name: 'replicaSet my-replica-set-v000',
account: 'my-account',
region: 'default',
cluster: 'replicaSet my-replica-set',
},
]),
);
const cluster = wrapper.find({ label: 'Cluster' }).find(ScopeClusterSelector).first();
expect(cluster.props().clusters).toEqual(['replicaSet my-replica-set']);
});
it("does not include cluster if selected kind does not match cluster's server groups' kind", () => {
const wrapper = component(
{
kind: 'statefulSet',
account: 'my-account',
location: 'default',
mode: SelectorMode.Dynamic,
},
buildPropsWithApplicationData([
{
name: 'replicaSet my-replica-set-v000',
account: 'my-account',
region: 'default',
cluster: 'replicaSet my-replica-set',
},
]),
);
const cluster = wrapper.find({ label: 'Cluster' }).find(ScopeClusterSelector).first();
expect(cluster.props().clusters).toEqual([]);
});
it('handles case in which a cluster has two different kinds of server groups', () => {
const wrapper = component(
{
kind: 'statefulSet',
account: 'my-account',
location: 'default',
mode: SelectorMode.Dynamic,
},
buildPropsWithApplicationData([
{
name: 'replicaSet my-replica-set-v000',
account: 'my-account',
region: 'default',
cluster: 'my-cluster',
},
{
name: 'statefulSet my-stateful-set-v000',
account: 'my-account',
region: 'default',
cluster: 'my-cluster',
},
]),
);
const cluster = wrapper.find({ label: 'Cluster' }).find(ScopeClusterSelector).first();
expect(cluster.props().clusters).toEqual(['my-cluster']);
});
it("does not include cluster if the cluster's server groups are managed", () => {
const wrapper = component(
{
kind: 'replicaSet',
account: 'my-account',
location: 'default',
mode: SelectorMode.Dynamic,
},
buildPropsWithApplicationData([
{
name: 'replicaSet my-replica-set-v000',
account: 'my-account',
region: 'default',
cluster: 'my-cluster',
serverGroupManagers: ['deployment my-deployment'],
},
]),
);
const cluster = wrapper.find({ label: 'Cluster' }).find(ScopeClusterSelector).first();
expect(cluster.props().clusters).toEqual([]);
});
it('filters clusters by account', () => {
const wrapper = component(
{
kind: 'replicaSet',
account: 'my-other-account',
location: 'default',
mode: SelectorMode.Dynamic,
},
buildPropsWithApplicationData([
{
name: 'replicaSet my-replica-set-v000',
account: 'my-account',
region: 'default',
cluster: 'my-cluster',
},
]),
);
const cluster = wrapper.find({ label: 'Cluster' }).find(ScopeClusterSelector).first();
expect(cluster.props().clusters).toEqual([]);
});
it('filters clusters by namespace', () => {
const wrapper = component(
{
kind: 'replicaSet',
account: 'my-account',
location: 'my-other-namespace',
mode: SelectorMode.Dynamic,
},
buildPropsWithApplicationData([
{
name: 'replicaSet my-replica-set-v000',
account: 'my-account',
region: 'default',
cluster: 'my-cluster',
},
]),
);
const cluster = wrapper.find({ label: 'Cluster' }).find(ScopeClusterSelector).first();
expect(cluster.props().clusters).toEqual([]);
});
});
});
describe('change handlers', () => {
it('calls the search service after updating the `Kind` field', () => {
const wrapper = component({
manifestName: 'configMap my-config-map',
account: 'my-account',
location: 'default',
});
const kind = wrapper.find({ label: 'Kind' }).find(Creatable).first();
kind.props().onChange({ value: 'deployment', label: 'deployment' });
expect(searchService).toHaveBeenCalledWith('deployment', 'default', 'my-account');
});
it('calls the search service after updating the `Namespace` field', () => {
const wrapper = component({
manifestName: 'configMap my-config-map',
account: 'my-account',
location: 'default',
});
const namespace = wrapper.find({ label: 'Namespace' }).find(Creatable).first();
namespace.props().onChange({ value: 'kube-system', label: 'kube-system' });
expect(searchService).toHaveBeenCalledWith('configMap', 'kube-system', 'my-account');
});
it('calls the search service after updating the `Account` field', () => {
const wrapper = component({
manifestName: 'configMap my-config-map',
account: 'my-account',
location: 'default',
});
wrapper.setState({
accounts: [
{ name: 'my-account', namespaces: ['default'] },
{ name: 'my-other-account', namespaces: ['default'] },
],
} as IManifestSelectorState);
const account = wrapper.find(AccountSelectInput).first();
account.props().onChange(createFakeReactSyntheticEvent({ value: 'my-other-account' }));
expect(searchService).toHaveBeenCalledWith('configMap', 'default', 'my-other-account');
});
it('clears namespace when changing account if account does not have selected namespace', () => {
const wrapper = component({
manifestName: 'configMap my-config-map',
account: 'my-account',
location: 'default',
});
wrapper.setState({
accounts: [
{ name: 'my-account', namespaces: ['default'] },
{ name: 'my-other-account', namespaces: ['other-default'] },
],
} as IManifestSelectorState);
const account = wrapper.find(AccountSelectInput).first();
account.props().onChange(createFakeReactSyntheticEvent({ value: 'my-other-account' }));
expect(wrapper.instance().state.selector.location).toBeFalsy();
});
});
describe('mode change', () => {
it('handles kind during static -> dynamic mode transition', () => {
const wrapper = component(
{
manifestName: 'configMap my-config-map',
account: 'my-account',
location: 'default',
},
{ modes: [SelectorMode.Dynamic, SelectorMode.Static] },
);
wrapper.find({ id: 'dynamic' }).first().props().onChange();
expect(wrapper.state().selector.kind).toEqual('configMap');
});
it('handles kind during static -> label mode transition', () => {
const wrapper = component(
{
manifestName: 'configMap my-config-map',
account: 'my-account',
location: 'default',
},
{ modes: [SelectorMode.Dynamic, SelectorMode.Static, SelectorMode.Label] },
);
wrapper.find({ id: 'label' }).first().props().onChange();
expect(wrapper.state().selector.kind).toBeNull();
expect(wrapper.state().selector.kinds).toEqual([]);
expect(wrapper.state().selector.manifestName).toBeNull();
});
it('handles kind during dynamic -> static mode transition', () => {
const wrapper = component(
{
account: 'my-account',
location: 'default',
kind: 'configMap',
mode: SelectorMode.Dynamic,
},
{ modes: [SelectorMode.Dynamic, SelectorMode.Static] },
);
wrapper.find({ id: 'static' }).first().props().onChange();
// `manifestName` is composed of `${kind} ${resourceName}`
expect(wrapper.state().selector.manifestName).toEqual('configMap');
});
it('handles kind during dynamic -> label mode transition', () => {
const wrapper = component(
{
account: 'my-account',
location: 'default',
kind: 'configMap',
mode: SelectorMode.Dynamic,
},
{ modes: [SelectorMode.Dynamic, SelectorMode.Static, SelectorMode.Label] },
);
wrapper.find({ id: 'label' }).first().props().onChange();
expect(wrapper.state().selector.kind).toBeNull();
expect(wrapper.state().selector.kinds).toEqual([]);
expect(wrapper.state().selector.manifestName).toBeNull();
});
it('handles kind during label -> static mode transition', () => {
const wrapper = component(
{
account: 'my-account',
location: 'default',
kinds: ['configMap'],
labelSelectors: {
selectors: [],
},
mode: SelectorMode.Label,
},
{ modes: [SelectorMode.Dynamic, SelectorMode.Static, SelectorMode.Label] },
);
wrapper.find({ id: 'static' }).first().props().onChange();
expect(wrapper.state().selector.kind).toBeNull();
expect(wrapper.state().selector.kinds).toBeNull();
});
it('handles kind during label -> dynamic mode transition', () => {
const wrapper = component(
{
account: 'my-account',
location: 'default',
kinds: ['configMap'],
labelSelectors: {
selectors: [],
},
mode: SelectorMode.Label,
},
{ modes: [SelectorMode.Dynamic, SelectorMode.Static, SelectorMode.Label] },
);
wrapper.find({ id: 'dynamic' }).first().props().onChange();
expect(wrapper.state().selector.kind).toBeNull();
expect(wrapper.state().selector.kinds).toBeNull();
});
});
});
const component = (selector: any, props: any = {}) =>
mount(() as any);