Skip to content

Commit

Permalink
AccessURLTable v2 (#1870)
Browse files Browse the repository at this point in the history
  • Loading branch information
Andres Martinez Gotor committed Jul 20, 2020
1 parent 814dad7 commit dcda5cc
Show file tree
Hide file tree
Showing 21 changed files with 1,762 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { IResource, IServiceSpec, IServiceStatus } from "shared/types";
import { IURLItem } from "./IURLItem";

// isLink returns true if there are any link in the Item
function isLink(loadBalancerService: IResource): boolean {
function isLink(loadBalancerService?: IResource): boolean {
if (
loadBalancerService &&
loadBalancerService.status &&
loadBalancerService.status.loadBalancer.ingress &&
loadBalancerService.status.loadBalancer.ingress.length
Expand All @@ -14,7 +15,10 @@ function isLink(loadBalancerService: IResource): boolean {
}

// URLs returns the list of URLs obtained from the service status
function URLs(loadBalancerService: IResource): string[] {
function URLs(loadBalancerService?: IResource): string[] {
if (!loadBalancerService) {
return ["Pending"];
}
const res: string[] = [];
const status: IServiceStatus = loadBalancerService.status;
if (status && status.loadBalancer.ingress && status.loadBalancer.ingress.length) {
Expand Down Expand Up @@ -42,9 +46,9 @@ function getURL(base: string, port: number) {
return `${protocol}://${base}${portSuffix}`;
}

export function GetURLItemFromService(loadBalancerService: IResource) {
export function GetURLItemFromService(loadBalancerService?: IResource) {
return {
name: loadBalancerService.metadata.name,
name: loadBalancerService?.metadata.name,
type: "Service LoadBalancer",
isLink: isLink(loadBalancerService),
URLs: URLs(loadBalancerService),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import AccessURLItem from "./AccessURLItem";
import { GetURLItemFromIngress } from "./AccessURLItem/AccessURLIngressHelper";
import { GetURLItemFromService } from "./AccessURLItem/AccessURLServiceHelper";

interface IAccessURLTableProps {
export interface IAccessURLTableProps {
services: Array<IKubeItem<IResource | IK8sList<IResource, {}>>>;
ingresses: Array<IKubeItem<IResource | IK8sList<IResource, {}>>>;
ingressRefs: ResourceRef[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { mount, shallow } from "enzyme";
import context from "jest-plugin-context";
import * as React from "react";

import itBehavesLike from "../../../shared/specs";

import ResourceRef from "shared/ResourceRef";
import { IIngressSpec, IResource, IServiceSpec, IServiceStatus } from "../../../shared/types";
import AccessURLTable from "./AccessURLTable.v2";

const defaultProps = {
services: [],
ingresses: [],
ingressRefs: [],
getResource: jest.fn(),
};

describe("when receiving ingresses", () => {
it("fetches ingresses at mount time", () => {
const ingress = { name: "ing" } as ResourceRef;
const mock = jest.fn();
mount(<AccessURLTable {...defaultProps} ingressRefs={[ingress]} getResource={mock} />);
expect(mock).toHaveBeenCalledWith(ingress);
});

it("fetches when new ingress refs received", () => {
const ingress = { name: "ing" } as ResourceRef;
const mock = jest.fn();
const wrapper = mount(<AccessURLTable {...defaultProps} getResource={mock} />);
wrapper.setProps({ ingressRefs: [ingress] });
expect(mock).toHaveBeenCalledWith(ingress);
});
});

context("when fetching ingresses or services", () => {
itBehavesLike("aLoadingComponent", {
component: AccessURLTable,
props: {
...defaultProps,
ingresses: [{ isFetching: true }],
},
});
itBehavesLike("aLoadingComponent", {
component: AccessURLTable,
props: {
...defaultProps,
services: [{ isFetching: true }],
},
});
});

it("doesn't render anything if the application has no URL", () => {
const wrapper = shallow(<AccessURLTable {...defaultProps} />);
expect(wrapper.find("table")).not.toExist();
});

context("when the app contains services", () => {
it("should omit the Service Table if there are no public services", () => {
const service = {
kind: "Service",
metadata: {
name: "foo",
selfLink: "/services/foo",
},
spec: {
type: "ClusterIP",
ports: [{ port: 8080 }],
} as IServiceSpec,
status: {
loadBalancer: {},
} as IServiceStatus,
} as IResource;
const services = [{ isFetching: false, item: service }];
const wrapper = shallow(<AccessURLTable {...defaultProps} services={services} />);
expect(wrapper.text()).toContain("The current application does not expose a public URL");
});

it("should show the table if any service is a LoadBalancer", () => {
const service = {
kind: "Service",
metadata: {
name: "foo",
selfLink: "/services/foo",
},
spec: {
type: "LoadBalancer",
ports: [{ port: 8080 }],
} as IServiceSpec,
status: {
loadBalancer: {},
} as IServiceStatus,
} as IResource;
const services = [{ isFetching: false, item: service }];
const wrapper = shallow(<AccessURLTable {...defaultProps} services={services} />);
expect(wrapper.find("Table")).toExist();
expect(wrapper).toMatchSnapshot();
});
});

context("when the app contains ingresses", () => {
it("should show the table with available ingresses", () => {
const ingress = {
kind: "Ingress",
metadata: {
name: "foo",
selfLink: "/ingresses/foo",
},
spec: {
rules: [
{
host: "foo.bar",
http: {
paths: [{ path: "/ready" }],
},
},
],
} as IIngressSpec,
} as IResource;
const ingresses = [{ isFetching: false, item: ingress }];
const wrapper = mount(<AccessURLTable {...defaultProps} ingresses={ingresses} />);
expect(wrapper.find("Table")).toExist();
expect(wrapper.find("a").findWhere(a => a.prop("href") === "http://foo.bar/ready")).toExist();
expect(wrapper).toMatchSnapshot();
});
});

context("when the app contains services and ingresses", () => {
it("should show the table with available svcs and ingresses", () => {
const service = {
kind: "Service",
metadata: {
name: "foo",
selfLink: "/services/foo",
},
spec: {
type: "LoadBalancer",
ports: [{ port: 8080 }],
} as IServiceSpec,
status: {
loadBalancer: {
ingress: [
{
ip: "1.2.3.4",
},
],
},
} as IServiceStatus,
} as IResource;
const services = [{ isFetching: false, item: service }];
const ingress = {
kind: "Ingress",
metadata: {
name: "foo",
selfLink: "/ingresses/foo",
},
spec: {
rules: [
{
host: "foo.bar",
http: {
paths: [{ path: "/ready" }],
},
},
],
} as IIngressSpec,
} as IResource;
const ingresses = [{ isFetching: false, item: ingress }];
const wrapper = mount(
<AccessURLTable {...defaultProps} services={services} ingresses={ingresses} />,
);
expect(wrapper.find("a").findWhere(a => a.prop("href") === "http://1.2.3.4:8080")).toExist();
expect(wrapper.find("a").findWhere(a => a.prop("href") === "http://foo.bar/ready")).toExist();
expect(wrapper).toMatchSnapshot();
});
});

context("when the app contains resources with errors", () => {
it("displays the error", () => {
const services = [{ isFetching: false, error: new Error("could not find Service") }];
const ingresses = [{ isFetching: false, error: new Error("could not find Ingress") }];
const wrapper = mount(
<AccessURLTable {...defaultProps} services={services} ingresses={ingresses} />,
);

// The Service error is not shown, as it is filtered out because without the
// resource we can't determine whether it is a public LoadBalancer Service
// or not. The Service error will be shown in the Services table anyway.
expect(wrapper.find("a")).not.toExist();
expect(wrapper.find("table").text()).toContain("could not find Ingress");

expect(wrapper).toMatchSnapshot();
});
});

0 comments on commit dcda5cc

Please sign in to comment.