mirror of
https://github.com/datahub-project/datahub.git
synced 2025-12-08 08:34:56 +00:00
fix(analytics): fix missing events from UI (#4026)
This commit is contained in:
parent
52272d6561
commit
452d2c22b5
@ -55,6 +55,10 @@ task yarnLint(type: YarnTask, dependsOn: [yarnInstall, yarnGenerate]) {
|
|||||||
args = ['run', 'lint']
|
args = ['run', 'lint']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task yarnLintFix(type: YarnTask, dependsOn: [yarnInstall, yarnGenerate]) {
|
||||||
|
args = ['run', 'lint-fix']
|
||||||
|
}
|
||||||
|
|
||||||
task yarnBuild(type: YarnTask, dependsOn: [yarnInstall, yarnTest, yarnLint]) {
|
task yarnBuild(type: YarnTask, dependsOn: [yarnInstall, yarnTest, yarnLint]) {
|
||||||
args = ['run', 'build']
|
args = ['run', 'build']
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,12 +122,15 @@ export interface EntitySectionViewEvent extends BaseEvent {
|
|||||||
*/
|
*/
|
||||||
export const EntityActionType = {
|
export const EntityActionType = {
|
||||||
UpdateTags: 'UpdateTags',
|
UpdateTags: 'UpdateTags',
|
||||||
|
UpdateTerms: 'UpdateTerms',
|
||||||
|
UpdateLinks: 'UpdateLinks',
|
||||||
UpdateOwnership: 'UpdateOwnership',
|
UpdateOwnership: 'UpdateOwnership',
|
||||||
UpdateDocumentation: 'UpdateDocumentation',
|
UpdateDocumentation: 'UpdateDocumentation',
|
||||||
UpdateDescription: 'UpdateDescription',
|
UpdateDescription: 'UpdateDescription',
|
||||||
UpdateProperties: 'UpdateProperties',
|
UpdateProperties: 'UpdateProperties',
|
||||||
UpdateSchemaDescription: 'UpdateSchemaDescription',
|
UpdateSchemaDescription: 'UpdateSchemaDescription',
|
||||||
UpdateSchemaTags: 'UpdateSchemaTags',
|
UpdateSchemaTags: 'UpdateSchemaTags',
|
||||||
|
UpdateSchemaTerms: 'UpdateSchemaTerms',
|
||||||
ClickExternalUrl: 'ClickExternalUrl',
|
ClickExternalUrl: 'ClickExternalUrl',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import {
|
|||||||
InstitutionalMemoryUpdate,
|
InstitutionalMemoryUpdate,
|
||||||
} from '../../../../types.generated';
|
} from '../../../../types.generated';
|
||||||
import { useEntityRegistry } from '../../../useEntityRegistry';
|
import { useEntityRegistry } from '../../../useEntityRegistry';
|
||||||
|
import { useEntityData } from '../../shared/EntityContext';
|
||||||
|
import analytics, { EventType, EntityActionType } from '../../../analytics';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
authenticatedUserUrn?: string;
|
authenticatedUserUrn?: string;
|
||||||
@ -93,6 +95,8 @@ export default function Documentation({
|
|||||||
setStagedDocs(newStagedDocs);
|
setStagedDocs(newStagedDocs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { urn, entityType } = useEntityData();
|
||||||
|
|
||||||
const onSave = async (record: any) => {
|
const onSave = async (record: any) => {
|
||||||
const row = await form.validateFields();
|
const row = await form.validateFields();
|
||||||
|
|
||||||
@ -113,6 +117,12 @@ export default function Documentation({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
updateDocumentation({ elements: updatedInstitutionalMemory });
|
updateDocumentation({ elements: updatedInstitutionalMemory });
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.EntityActionEvent,
|
||||||
|
actionType: EntityActionType.UpdateDescription,
|
||||||
|
entityType,
|
||||||
|
entityUrn: urn,
|
||||||
|
});
|
||||||
setEditingIndex(-1);
|
setEditingIndex(-1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -130,6 +140,12 @@ export default function Documentation({
|
|||||||
description: doc.description,
|
description: doc.description,
|
||||||
}));
|
}));
|
||||||
updateDocumentation({ elements: updatedInstitutionalMemory });
|
updateDocumentation({ elements: updatedInstitutionalMemory });
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.EntityActionEvent,
|
||||||
|
actionType: EntityActionType.UpdateDescription,
|
||||||
|
entityType,
|
||||||
|
entityUrn: urn,
|
||||||
|
});
|
||||||
setStagedDocs(newDocs);
|
setStagedDocs(newDocs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import UpdateDescriptionModal from '../../../../shared/components/legacy/Descrip
|
|||||||
import StripMarkdownText, { removeMarkdown } from '../../../../shared/components/styled/StripMarkdownText';
|
import StripMarkdownText, { removeMarkdown } from '../../../../shared/components/styled/StripMarkdownText';
|
||||||
import MarkdownViewer from '../../../../shared/components/legacy/MarkdownViewer';
|
import MarkdownViewer from '../../../../shared/components/legacy/MarkdownViewer';
|
||||||
import SchemaEditableContext from '../../../../../shared/SchemaEditableContext';
|
import SchemaEditableContext from '../../../../../shared/SchemaEditableContext';
|
||||||
|
import { useEntityData } from '../../../../shared/EntityContext';
|
||||||
|
import analytics, { EventType, EntityActionType } from '../../../../../analytics';
|
||||||
|
|
||||||
const EditIcon = styled(EditOutlined)`
|
const EditIcon = styled(EditOutlined)`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -89,6 +91,16 @@ export default function DescriptionField({ description, onUpdate, isEdited = fal
|
|||||||
const [expanded, setExpanded] = useState(!overLimit);
|
const [expanded, setExpanded] = useState(!overLimit);
|
||||||
const isSchemaEditable = React.useContext(SchemaEditableContext);
|
const isSchemaEditable = React.useContext(SchemaEditableContext);
|
||||||
const onCloseModal = () => setShowAddModal(false);
|
const onCloseModal = () => setShowAddModal(false);
|
||||||
|
const { urn, entityType } = useEntityData();
|
||||||
|
|
||||||
|
const sendAnalytics = () => {
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.EntityActionEvent,
|
||||||
|
actionType: EntityActionType.UpdateSchemaDescription,
|
||||||
|
entityType,
|
||||||
|
entityUrn: urn,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const onUpdateModal = async (desc: string | null) => {
|
const onUpdateModal = async (desc: string | null) => {
|
||||||
message.loading({ content: 'Updating...' });
|
message.loading({ content: 'Updating...' });
|
||||||
@ -96,6 +108,7 @@ export default function DescriptionField({ description, onUpdate, isEdited = fal
|
|||||||
await onUpdate(desc || '');
|
await onUpdate(desc || '');
|
||||||
message.destroy();
|
message.destroy();
|
||||||
message.success({ content: 'Updated!', duration: 2 });
|
message.success({ content: 'Updated!', duration: 2 });
|
||||||
|
sendAnalytics();
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
message.destroy();
|
message.destroy();
|
||||||
if (e instanceof Error) message.error({ content: `Update Failed! \n ${e.message || ''}`, duration: 2 });
|
if (e instanceof Error) message.error({ content: `Update Failed! \n ${e.message || ''}`, duration: 2 });
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { PlusOutlined } from '@ant-design/icons';
|
|||||||
import { useGetAuthenticatedUser } from '../../../../useGetAuthenticatedUser';
|
import { useGetAuthenticatedUser } from '../../../../useGetAuthenticatedUser';
|
||||||
import { useEntityData } from '../../EntityContext';
|
import { useEntityData } from '../../EntityContext';
|
||||||
import { useAddLinkMutation } from '../../../../../graphql/mutations.generated';
|
import { useAddLinkMutation } from '../../../../../graphql/mutations.generated';
|
||||||
|
import analytics, { EventType, EntityActionType } from '../../../../analytics';
|
||||||
|
|
||||||
type AddLinkProps = {
|
type AddLinkProps = {
|
||||||
buttonProps?: Record<string, unknown>;
|
buttonProps?: Record<string, unknown>;
|
||||||
@ -13,7 +14,7 @@ type AddLinkProps = {
|
|||||||
export const AddLinkModal = ({ buttonProps, refetch }: AddLinkProps) => {
|
export const AddLinkModal = ({ buttonProps, refetch }: AddLinkProps) => {
|
||||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||||
const user = useGetAuthenticatedUser();
|
const user = useGetAuthenticatedUser();
|
||||||
const { urn } = useEntityData();
|
const { urn, entityType } = useEntityData();
|
||||||
const [addLinkMutation] = useAddLinkMutation();
|
const [addLinkMutation] = useAddLinkMutation();
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
@ -34,6 +35,12 @@ export const AddLinkModal = ({ buttonProps, refetch }: AddLinkProps) => {
|
|||||||
variables: { input: { linkUrl: formData.url, label: formData.label, resourceUrn: urn } },
|
variables: { input: { linkUrl: formData.url, label: formData.label, resourceUrn: urn } },
|
||||||
});
|
});
|
||||||
message.success({ content: 'Link Added', duration: 2 });
|
message.success({ content: 'Link Added', duration: 2 });
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.EntityActionEvent,
|
||||||
|
entityType,
|
||||||
|
entityUrn: urn,
|
||||||
|
actionType: EntityActionType.UpdateLinks,
|
||||||
|
});
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
message.destroy();
|
message.destroy();
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import { useRemoveOwnerMutation } from '../../../../../graphql/mutations.generat
|
|||||||
import { EntityType, Owner } from '../../../../../types.generated';
|
import { EntityType, Owner } from '../../../../../types.generated';
|
||||||
import { CustomAvatar } from '../../../../shared/avatar';
|
import { CustomAvatar } from '../../../../shared/avatar';
|
||||||
import { useEntityRegistry } from '../../../../useEntityRegistry';
|
import { useEntityRegistry } from '../../../../useEntityRegistry';
|
||||||
|
import analytics, { EventType, EntityActionType } from '../../../../analytics';
|
||||||
|
import { useEntityData } from '../../EntityContext';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
entityUrn: string;
|
entityUrn: string;
|
||||||
@ -24,6 +26,7 @@ const OwnerTag = styled(Tag)`
|
|||||||
|
|
||||||
export const ExpandedOwner = ({ entityUrn, owner, refetch }: Props) => {
|
export const ExpandedOwner = ({ entityUrn, owner, refetch }: Props) => {
|
||||||
const entityRegistry = useEntityRegistry();
|
const entityRegistry = useEntityRegistry();
|
||||||
|
const { entityType } = useEntityData();
|
||||||
const [removeOwnerMutation] = useRemoveOwnerMutation();
|
const [removeOwnerMutation] = useRemoveOwnerMutation();
|
||||||
|
|
||||||
let name = '';
|
let name = '';
|
||||||
@ -47,6 +50,12 @@ export const ExpandedOwner = ({ entityUrn, owner, refetch }: Props) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
message.success({ content: 'Owner Removed', duration: 2 });
|
message.success({ content: 'Owner Removed', duration: 2 });
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.EntityActionEvent,
|
||||||
|
actionType: EntityActionType.UpdateOwnership,
|
||||||
|
entityType,
|
||||||
|
entityUrn,
|
||||||
|
});
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
message.destroy();
|
message.destroy();
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { useEntityRegistry } from '../../../../useEntityRegistry';
|
|||||||
import LineageExplorer from '../../../../lineage/LineageExplorer';
|
import LineageExplorer from '../../../../lineage/LineageExplorer';
|
||||||
import CompactContext from '../../../../shared/CompactContext';
|
import CompactContext from '../../../../shared/CompactContext';
|
||||||
import DynamicTab from '../../tabs/Entity/weaklyTypedAspects/DynamicTab';
|
import DynamicTab from '../../tabs/Entity/weaklyTypedAspects/DynamicTab';
|
||||||
|
import analytics, { EventType } from '../../../../analytics';
|
||||||
|
|
||||||
type Props<T, U> = {
|
type Props<T, U> = {
|
||||||
urn: string;
|
urn: string;
|
||||||
@ -124,6 +125,12 @@ export const EntityProfile = <T, U>({
|
|||||||
tabParams?: Record<string, any>;
|
tabParams?: Record<string, any>;
|
||||||
method?: 'push' | 'replace';
|
method?: 'push' | 'replace';
|
||||||
}) => {
|
}) => {
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.EntitySectionViewEvent,
|
||||||
|
entityType,
|
||||||
|
entityUrn: urn,
|
||||||
|
section: tabName.toLowerCase(),
|
||||||
|
});
|
||||||
history[method](getEntityPath(entityType, urn, entityRegistry, false, tabName, tabParams));
|
history[method](getEntityPath(entityType, urn, entityRegistry, false, tabName, tabParams));
|
||||||
},
|
},
|
||||||
[history, entityType, urn, entityRegistry],
|
[history, entityType, urn, entityRegistry],
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { IconStyleType } from '../../../../Entity';
|
|||||||
import { ANTD_GRAY } from '../../../constants';
|
import { ANTD_GRAY } from '../../../constants';
|
||||||
import { useEntityData } from '../../../EntityContext';
|
import { useEntityData } from '../../../EntityContext';
|
||||||
import { useEntityPath } from '../utils';
|
import { useEntityPath } from '../utils';
|
||||||
|
import analytics, { EventType, EntityActionType } from '../../../../../analytics';
|
||||||
|
|
||||||
const LogoContainer = styled.span`
|
const LogoContainer = styled.span`
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
@ -105,6 +106,16 @@ export const EntityHeader = () => {
|
|||||||
const entityPath = useEntityPath(entityType, urn);
|
const entityPath = useEntityPath(entityType, urn);
|
||||||
const externalUrl = entityData?.externalUrl || undefined;
|
const externalUrl = entityData?.externalUrl || undefined;
|
||||||
const hasExternalUrl = !!externalUrl;
|
const hasExternalUrl = !!externalUrl;
|
||||||
|
|
||||||
|
const sendAnalytics = () => {
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.EntityActionEvent,
|
||||||
|
actionType: EntityActionType.ClickExternalUrl,
|
||||||
|
entityType,
|
||||||
|
entityUrn: urn,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const entityCount = entityData?.entityCount;
|
const entityCount = entityData?.entityCount;
|
||||||
const typeIcon = entityRegistry.getIcon(entityType, 12, IconStyleType.ACCENT);
|
const typeIcon = entityRegistry.getIcon(entityType, 12, IconStyleType.ACCENT);
|
||||||
const container = entityData?.container;
|
const container = entityData?.container;
|
||||||
@ -146,7 +157,11 @@ export const EntityHeader = () => {
|
|||||||
<EntityTitle level={3}>{entityData?.name || ' '}</EntityTitle>
|
<EntityTitle level={3}>{entityData?.name || ' '}</EntityTitle>
|
||||||
</Link>
|
</Link>
|
||||||
</MainHeaderContent>
|
</MainHeaderContent>
|
||||||
{hasExternalUrl && <ExternalLinkButton href={externalUrl}>View in {platformName}</ExternalLinkButton>}
|
{hasExternalUrl && (
|
||||||
|
<ExternalLinkButton href={externalUrl} onClick={sendAnalytics}>
|
||||||
|
View in {platformName}
|
||||||
|
</ExternalLinkButton>
|
||||||
|
)}
|
||||||
<Tooltip title="Copy URN. An URN uniquely identifies an entity on DataHub.">
|
<Tooltip title="Copy URN. An URN uniquely identifies an entity on DataHub.">
|
||||||
<Button
|
<Button
|
||||||
icon={copiedUrn ? <CheckOutlined /> : <CopyOutlined />}
|
icon={copiedUrn ? <CheckOutlined /> : <CopyOutlined />}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { CorpUser, EntityType, OwnerEntityType, SearchResult } from '../../../..
|
|||||||
import { useEntityRegistry } from '../../../../../../useEntityRegistry';
|
import { useEntityRegistry } from '../../../../../../useEntityRegistry';
|
||||||
import { useEntityData } from '../../../../EntityContext';
|
import { useEntityData } from '../../../../EntityContext';
|
||||||
import { CustomAvatar } from '../../../../../../shared/avatar';
|
import { CustomAvatar } from '../../../../../../shared/avatar';
|
||||||
|
import analytics, { EventType, EntityActionType } from '../../../../../../analytics';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
@ -40,7 +41,7 @@ type SelectedActor = {
|
|||||||
|
|
||||||
export const AddOwnerModal = ({ visible, onClose, refetch }: Props) => {
|
export const AddOwnerModal = ({ visible, onClose, refetch }: Props) => {
|
||||||
const entityRegistry = useEntityRegistry();
|
const entityRegistry = useEntityRegistry();
|
||||||
const { urn } = useEntityData();
|
const { urn, entityType } = useEntityData();
|
||||||
const [selectedActor, setSelectedActor] = useState<SelectedActor | undefined>(undefined);
|
const [selectedActor, setSelectedActor] = useState<SelectedActor | undefined>(undefined);
|
||||||
const [userSearch, { data: userSearchData }] = useGetSearchResultsLazyQuery();
|
const [userSearch, { data: userSearchData }] = useGetSearchResultsLazyQuery();
|
||||||
const [groupSearch, { data: groupSearchData }] = useGetSearchResultsLazyQuery();
|
const [groupSearch, { data: groupSearchData }] = useGetSearchResultsLazyQuery();
|
||||||
@ -70,6 +71,12 @@ export const AddOwnerModal = ({ visible, onClose, refetch }: Props) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
message.success({ content: 'Owner Added', duration: 2 });
|
message.success({ content: 'Owner Added', duration: 2 });
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.EntityActionEvent,
|
||||||
|
actionType: EntityActionType.UpdateOwnership,
|
||||||
|
entityType,
|
||||||
|
entityUrn: urn,
|
||||||
|
});
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
message.destroy();
|
message.destroy();
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
|
|||||||
@ -151,6 +151,7 @@ export default function AddTagTermModal({
|
|||||||
|
|
||||||
let urnToAdd = '';
|
let urnToAdd = '';
|
||||||
let input = {};
|
let input = {};
|
||||||
|
let actionType = EntityActionType.UpdateSchemaTags;
|
||||||
if (selectedType === EntityType.Tag) {
|
if (selectedType === EntityType.Tag) {
|
||||||
urnToAdd = `urn:li:tag:${selectedName}`;
|
urnToAdd = `urn:li:tag:${selectedName}`;
|
||||||
input = {
|
input = {
|
||||||
@ -159,6 +160,11 @@ export default function AddTagTermModal({
|
|||||||
subResource: entitySubresource,
|
subResource: entitySubresource,
|
||||||
subResourceType: entitySubresource ? SubResourceType.DatasetField : null,
|
subResourceType: entitySubresource ? SubResourceType.DatasetField : null,
|
||||||
};
|
};
|
||||||
|
if (entitySubresource) {
|
||||||
|
actionType = EntityActionType.UpdateSchemaTags;
|
||||||
|
} else {
|
||||||
|
actionType = EntityActionType.UpdateTags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (selectedType === EntityType.GlossaryTerm) {
|
if (selectedType === EntityType.GlossaryTerm) {
|
||||||
urnToAdd = `urn:li:glossaryTerm:${selectedName}`;
|
urnToAdd = `urn:li:glossaryTerm:${selectedName}`;
|
||||||
@ -168,14 +174,20 @@ export default function AddTagTermModal({
|
|||||||
subResource: entitySubresource,
|
subResource: entitySubresource,
|
||||||
subResourceType: entitySubresource ? SubResourceType.DatasetField : null,
|
subResourceType: entitySubresource ? SubResourceType.DatasetField : null,
|
||||||
};
|
};
|
||||||
|
if (entitySubresource) {
|
||||||
|
actionType = EntityActionType.UpdateSchemaTerms;
|
||||||
|
} else {
|
||||||
|
actionType = EntityActionType.UpdateTerms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
analytics.event({
|
analytics.event({
|
||||||
type: EventType.EntityActionEvent,
|
type: EventType.EntityActionEvent,
|
||||||
actionType: EntityActionType.UpdateTags,
|
|
||||||
entityType,
|
entityType,
|
||||||
entityUrn,
|
entityUrn,
|
||||||
|
actionType,
|
||||||
});
|
});
|
||||||
|
|
||||||
mutation({
|
mutation({
|
||||||
variables: {
|
variables: {
|
||||||
input,
|
input,
|
||||||
|
|||||||
@ -117,7 +117,7 @@ export default function TagTermGroup({
|
|||||||
const termToRemove = editableGlossaryTerms?.terms?.find((term) => term.term.urn === urnToRemove);
|
const termToRemove = editableGlossaryTerms?.terms?.find((term) => term.term.urn === urnToRemove);
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: `Do you want to remove ${termToRemove?.term.name} term?`,
|
title: `Do you want to remove ${termToRemove?.term.name} term?`,
|
||||||
content: `Are you sure you want to remove the ${termToRemove?.term.name} tag?`,
|
content: `Are you sure you want to remove the ${termToRemove?.term.name} term?`,
|
||||||
onOk() {
|
onOk() {
|
||||||
if (entityUrn) {
|
if (entityUrn) {
|
||||||
removeTermMutation({
|
removeTermMutation({
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
describe('analytics', () => {
|
||||||
|
it('can go to a dataset and see analytics in Section Views', () => {
|
||||||
|
cy.login();
|
||||||
|
|
||||||
|
cy.visit("/analytics");
|
||||||
|
cy.contains("documentation").should('not.exist');
|
||||||
|
|
||||||
|
cy.visit("/chart/urn:li:chart:(looker,baz1)");
|
||||||
|
cy.get("#rc-tabs-0-panel-Dashboards").click({ force: true });
|
||||||
|
|
||||||
|
cy.visit("/analytics");
|
||||||
|
cy.contains("documentation");
|
||||||
|
});
|
||||||
|
})
|
||||||
Loading…
x
Reference in New Issue
Block a user