Skip to content

Commit

Permalink
Improve E2E setup
Browse files Browse the repository at this point in the history
  • Loading branch information
yannikmesserli committed Feb 26, 2024
1 parent 2f18e69 commit 9a904ee
Show file tree
Hide file tree
Showing 40 changed files with 410 additions and 500 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ yarn-error.log*
.vscode

# Cypress
src/testing/e2e/cypress/screenshots
src/testing/e2e/screenshots
9 changes: 6 additions & 3 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import { defineConfig } from 'cypress';
import webpackPreprocessor from '@cypress/webpack-preprocessor';

import mainWebpackConfig from './webpack.config';
import HtmlWebpackPlugin from 'html-webpack-plugin';

export const webpackConfiguration = async (f: Cypress.FileObject) => {
const opts = { webpackOptions: { ...mainWebpackConfig, plugins: [] } };
// HtmlWebpackPlugin is clashing with cypress own html file process
const plugins = mainWebpackConfig.plugins.filter(f => !(f instanceof HtmlWebpackPlugin));
const opts = { webpackOptions: { ...mainWebpackConfig, plugins } };

return await webpackPreprocessor(opts as any)(f);
};

export default defineConfig({
projectId: '6to9oy',
e2e: {
specPattern: `src/testing/e2e/cypress/scenarios/**/*.cy.{js,jsx,ts,tsx}`,
specPattern: `src/testing/e2e/scenarios/**/*.cy.{js,jsx,ts,tsx}`,
baseUrl: process.env.BASE_URL ?? 'http://127.0.0.1:8080',
supportFile: 'src/testing/e2e/cypress/support/index.ts',
supportFile: 'src/testing/e2e/support/index.ts',
chromeWebSecurity: false,
env: {
username: 'admin@example.com',
Expand Down
9 changes: 5 additions & 4 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import type { Config } from 'jest';
import { pathsToModuleNameMapper } from 'ts-jest';
import tsconfig from './tsconfig.json';

const config: Config = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
testMatch: [
'<rootDir>/src/**/?(*.)(spec|test).(j|t)s?(x)',
'<rootDir>/src/**/*.(spec|test).(j|t)s?(x)',
'<rootDir>/src/**/__tests__/**/*.(j|t)s?(x)',
],
roots: ['<rootDir>/src'],
moduleNameMapper: {
'\\.(png|jpg|gif|ttf|woff|woff2)$': '<rootDir>/scripts/assets-transformer.js',
'\\.(css|styl|less|sass|scss)$': 'identity-obj-proxy',
'\\.svg$': '<rootDir>/scripts/svg-mock.js',
'^~/(.*)$': '<rootDir>/src/$1',
'^~backend/(.*)$': '<rootDir>/backend/$1',
'^~e2e/(.*)$': `<rootDir>/src/testing/e2e/$1`,
...pathsToModuleNameMapper(tsconfig.compilerOptions.paths, { prefix: '<rootDir>/src' }),
},
modulePaths: [tsconfig.compilerOptions.baseUrl],
transform: {
'^.+\\.tsx?$': [
'ts-jest',
Expand Down
61 changes: 47 additions & 14 deletions src/components/AccessPoint/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import React, { useLayoutEffect, useRef } from 'react';
import * as mobx from 'mobx';
import { observer } from 'mobx-react';

import * as e2e from '~e2e/client';

import { XY } from '~/domain/geometry';
import { IPProtocol, L7Kind, Verdict } from '~/domain/hubble';
import * as helpers from '~/domain/helpers';
import { AuthType, IPProtocol, L7Kind, Verdict } from '~/domain/hubble';
import * as l7helpers from '~/domain/helpers/l7';

import css from './styles.scss';
import { getTestAttributes } from '~/utils/test';

export interface Props {
port: number;
Expand All @@ -18,6 +17,38 @@ export interface Props {
connectorRef?: React.MutableRefObject<HTMLDivElement | null>;
}

export enum E2E {
accessPointPortTestId = 'port',
accessPointL4ProtoTestId = 'proto-l4',
accessPointL7ProtoTestId = 'proto-l7',
}

export const getAccessPointTestAttributes = (
port: number,
l4protocol: IPProtocol,
l7protocol?: L7Kind,
_verdicts?: Set<Verdict>,
_authTypes?: Set<AuthType>,
) => {
const obj: any = {
port,
l4protocol: helpers.protocol.toString(l4protocol),
};

if (l7protocol != null) {
obj.l7protocol = helpers.l7.l7KindToString(l7protocol);
}

if (_verdicts != null) {
obj.verdicts =
Array.from(_verdicts || [])
.map(v => helpers.verdict.toString(v))
.join('-') || void 0;
}

return obj;
};

export const AccessPoint = observer(function AccessPoint(props: Props) {
const connectorRef = useRef<HTMLDivElement>(null);

Expand All @@ -30,19 +61,21 @@ export const AccessPoint = observer(function AccessPoint(props: Props) {
props.connectorRef.current = connectorRef.current;
}, [props.connectorRef]);

const e2eAttrs = mobx
const dataTestAttributes = mobx
.computed(() => {
return e2e.attributes.serviceMap.accessPoint(
props.port,
props.l4Protocol,
props.l7Protocol,
props.verdicts,
return getTestAttributes(
getAccessPointTestAttributes(
props.port,
props.l4Protocol,
props.l7Protocol,
props.verdicts,
),
);
})
.get();

return (
<div className={css.accessPoint} {...e2eAttrs}>
<div className={css.accessPoint} {...dataTestAttributes}>
<div className={css.icons}>
<div className={css.circle} ref={connectorRef}>
<img src="icons/misc/access-point.svg" />
Expand All @@ -56,20 +89,20 @@ export const AccessPoint = observer(function AccessPoint(props: Props) {
<div className={css.data}>
{showPort && (
<>
<div className={css.port} {...e2e.attributes.serviceMap.portSelector()}>
<div className={css.port} {...getTestAttributes(E2E.accessPointPortTestId)}>
{props.port}
</div>
<div className={css.dot} />
</>
)}
<div className={css.protocol} {...e2e.attributes.serviceMap.l4ProtoSelector()}>
<div className={css.protocol} {...getTestAttributes(E2E.accessPointL4ProtoTestId)}>
{IPProtocol[props.l4Protocol]}
</div>

{props.l7Protocol && showL7Protocol && (
<>
<div className={css.dot} />
<div className={css.protocol} {...e2e.attributes.serviceMap.l7ProtoSelector()}>
<div className={css.protocol} {...getTestAttributes(E2E.accessPointL7ProtoTestId)}>
{l7helpers.l7KindToString(props.l7Protocol)}
</div>
</>
Expand Down
13 changes: 11 additions & 2 deletions src/components/Card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import { observer } from 'mobx-react';

import { XYWH } from '~/domain/geometry';
import { sizes } from '~/ui';
import * as e2e from '~e2e/client';

import type { CardProps, DivRef, SVGGElementRef, CoordsFn } from './general';

import css from './styles.scss';
import { getTestAttributes } from '~/utils/test';

export type { CardProps, DivRef, SVGGElementRef, CoordsFn };

export enum E2E {
cardRootTestId = 'card-div-root',
}

export const Card = observer(function Card<C>(props: CardProps<C>) {
const divRef = useRef<HTMLDivElement>(null);

Expand All @@ -38,7 +42,12 @@ export const Card = observer(function Card<C>(props: CardProps<C>) {
return (
<g transform={`translate(${viewX}, ${viewY})`} onClick={() => props.onClick?.(props.card)}>
<foreignObject width={viewW} height={viewH}>
<div className={classes} ref={divRef} style={styles} {...e2e.attributes.card.selector()}>
<div
className={classes}
ref={divRef}
style={styles}
{...getTestAttributes(E2E.cardRootTestId)}
>
{props.children}
</div>
</foreignObject>
Expand Down
15 changes: 13 additions & 2 deletions src/components/EndpointCardHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import { ServiceCard } from '~/domain/service-map';

import { EndpointLogo } from './EndpointLogo';

import * as e2e from '~e2e/client';
import css from './styles.scss';
import { getTestAttributes } from '~/utils/test';

export enum E2E {
cardHeaderTestSelector = 'card-header',
cardIdTestSelector = 'card-id',
}

export interface Props {
card: ServiceCard;
Expand All @@ -23,7 +28,13 @@ export const EndpointCardHeader = memo(function EndpointCardHeader(props: Props)
});

return (
<div className={css.wrapper} {...e2e.attributes.serviceMap.card(card)}>
<div
className={css.wrapper}
{...getTestAttributes({
[E2E.cardHeaderTestSelector]: card.caption,
[E2E.cardIdTestSelector]: card.id,
})}
>
<div className={css.headline} onClick={props.onHeadlineClick}>
<EndpointLogo card={card} />

Expand Down
12 changes: 8 additions & 4 deletions src/components/Map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ import { useMapZoom } from './hooks/useMapZoom';
import { useMutationObserver } from '~/ui/hooks/useMutationObserver';
import { ArrowStrategy, PlacementStrategy } from '~/ui/layout';

import * as e2e from '~e2e/client';

import { sizes } from '~/ui/vars';
import css from './styles.scss';
import { getTestAttributes } from '~/utils/test';

export enum E2E {
visibleCardsTestId = 'visible-cards',
arrowForegroundTestId = 'arrows-foreground',
}

export interface Props<C extends AbstractCard> {
placement: PlacementStrategy;
Expand Down Expand Up @@ -112,9 +116,9 @@ export const MapElements = observer(function MapElements<C extends AbstractCard>
<g
className="arrows-foreground"
ref={arrowsForegroundRef}
{...e2e.attributes.map.arrowsForegroundSelector()}
{...getTestAttributes(E2E.arrowForegroundTestId)}
/>
<g ref={cardsRef} className="visible-cards" {...e2e.attributes.card.visibleContainer()}>
<g ref={cardsRef} className="visible-cards" {...getTestAttributes(E2E.visibleCardsTestId)}>
{cards}
</g>

Expand Down
17 changes: 13 additions & 4 deletions src/components/ServiceMapApp/WelcomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ import { observer } from 'mobx-react';

import { NamespaceDescriptor } from '~/domain/namespaces';

import { e2e } from '~e2e/client';

import css from './WelcomeScreen.scss';
import hubbleLogo from '~/assets/images/hubble-logo.png';
import { getTestAttributes } from '~/utils/test';

export enum E2E {
namespaceAvailabilityTestSelector = 'availability',
namespaceAvailabilityTestValue = 'r',
namespaceNameTestSelector = 'name',
namespaceListTestId = 'ns-list',
}

export interface Props {
namespaces: NamespaceDescriptor[];
Expand All @@ -24,7 +30,7 @@ export const WelcomeScreen = observer(function WelcomeScreen(props: Props) {
<h1 className={css.title}>Welcome!</h1>
<p className={css.description}>To begin select one of the namespaces:</p>
{someNamespacesLoaded ? (
<ul className={css.namespacesList} {...e2e.attributes.ns.listSelector()}>
<ul className={css.namespacesList} {...getTestAttributes(E2E.namespaceListTestId)}>
{props.namespaces.map(ns => (
<NamespaceItem key={ns.namespace} namespace={ns} onClick={props.onNamespaceChange} />
))}
Expand Down Expand Up @@ -58,7 +64,10 @@ const NamespaceItem = observer(function NamespaceItem(props: NamespaceItemProps)
<a
href={`/${props.namespace.namespace}`}
onClick={onClick}
{...e2e.attributes.ns.entry(props.namespace.namespace)}
{...getTestAttributes({
[E2E.namespaceAvailabilityTestSelector]: E2E.namespaceAvailabilityTestValue,
[E2E.namespaceNameTestSelector]: props.namespace.namespace,
})}
>
{props.namespace.namespace}
</a>
Expand Down
36 changes: 27 additions & 9 deletions src/components/ServiceMapArrowRenderer/ServiceMapArrowDuckFeet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,22 @@ import { ArrowRendererProps } from '~/components/ArrowsRenderer';
import { Teleport } from '~/components/Teleport';

import { reactionRef } from '~/ui/react/refs';
import { e2e } from '~/testing/e2e/client';

import * as helpers from './helpers';
import css from './styles.scss';
import { getTestAttributes } from '~/utils/test';

export type Props = Omit<ArrowRendererProps, 'arrow'> & {
arrows: Map<string, AccessPointArrow>;
connectorId: string;
};

export enum E2E {
duckFeetTestSelector = 'duck-feet-to-connector-id',
accessPointTestId = 'ap-lines',
innerLineTestSelector = 'inner-line',
}

export const ServiceMapArrowDuckFeet = observer(function ServiceMapArrowDuckFeet(props: Props) {
const connectorCapRef = reactionRef<SVGGElement | null>(null, e => {
renderEndingConnectorCap(e, props.connectorId, props.arrows);
Expand All @@ -32,8 +38,6 @@ export const ServiceMapArrowDuckFeet = observer(function ServiceMapArrowDuckFeet
renderInnerArrows(e, props.arrows);
});

const e2eAttrs = e2e.attributes.serviceMap;

const renderEndingConnectorCap = useCallback(
(target: SVGGElement | null, connectorId: string, arrows: Map<string, AccessPointArrow>) => {
const connectorPosition = MapUtils.pickFirst(arrows)?.start;
Expand Down Expand Up @@ -109,10 +113,21 @@ export const ServiceMapArrowDuckFeet = observer(function ServiceMapArrowDuckFeet
.enter()
.append('line')
.attr('class', 'inner')
.attr(
e2eAttrs.innerLineAttrName(),
e2e.attributes.nullOr(arr => arr.accessPointId),
)
.each(function (d: any) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self: any = this;
const e2eAttributes =
d.accessPointId &&
getTestAttributes({
[E2E.innerLineTestSelector]: d.accessPointId,
});

if (e2eAttributes) {
Object.keys(e2eAttributes).forEach(e2eAttr => {
d3.select(self).attr(e2eAttr, e2eAttributes[e2eAttr]);
});
}
})
.attr('stroke', helpers.innerArrows.strokeColor)
.attr('stroke-width', helpers.innerArrows.strokeWidth)
.attr('stroke-dasharray', helpers.innerArrows.strokeStyle)
Expand Down Expand Up @@ -166,11 +181,14 @@ export const ServiceMapArrowDuckFeet = observer(function ServiceMapArrowDuckFeet

return (
<Teleport to={props.arrowsForeground}>
<g className={classes} {...e2eAttrs.duckFeet(props.connectorId)}>
<g
className={classes}
{...getTestAttributes({ [E2E.duckFeetTestSelector]: props.connectorId })}
>
<g
className="arrows-to-access-points"
ref={innerArrowsRef}
{...e2eAttrs.linesToAccessPointsSelector()}
{...getTestAttributes(E2E.accessPointTestId)}
></g>
<g className="connector-cap" ref={connectorCapRef}></g>
</g>
Expand Down
Loading

0 comments on commit 9a904ee

Please sign in to comment.