From 56d1fb7834945d787064366547243ae3d1fe07e3 Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Tue, 31 May 2022 17:09:25 +0000 Subject: [PATCH 1/8] add a divider after Account menu item --- site/src/components/UserDropdown/UsersDropdown.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/src/components/UserDropdown/UsersDropdown.tsx b/site/src/components/UserDropdown/UsersDropdown.tsx index 3e1de2c41162a..34eb58ea2b9e3 100644 --- a/site/src/components/UserDropdown/UsersDropdown.tsx +++ b/site/src/components/UserDropdown/UsersDropdown.tsx @@ -78,6 +78,8 @@ export const UserDropdown: React.FC = ({ user, onSignOut }: U + + Date: Tue, 31 May 2022 20:00:16 +0000 Subject: [PATCH 2/8] test: improve Storybook tests --- .../src/components/UserDropdown/UserDropdown.stories.tsx | 9 +++++++++ site/src/components/UserDropdown/UsersDropdown.tsx | 5 +++-- .../UserProfileCard/UserProfileCard.stories.tsx | 6 ++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/site/src/components/UserDropdown/UserDropdown.stories.tsx b/site/src/components/UserDropdown/UserDropdown.stories.tsx index 1ba9ee38b7c1a..cce4ace4dddc4 100644 --- a/site/src/components/UserDropdown/UserDropdown.stories.tsx +++ b/site/src/components/UserDropdown/UserDropdown.stories.tsx @@ -24,3 +24,12 @@ ExampleNoRoles.args = { return Promise.resolve() }, } + +export const Open = Template.bind({}) +Open.args = { + isOpen: true, + user: MockUser, + onSignOut: () => { + return Promise.resolve() + }, +} diff --git a/site/src/components/UserDropdown/UsersDropdown.tsx b/site/src/components/UserDropdown/UsersDropdown.tsx index 34eb58ea2b9e3..8a8bd310d5e9a 100644 --- a/site/src/components/UserDropdown/UsersDropdown.tsx +++ b/site/src/components/UserDropdown/UsersDropdown.tsx @@ -22,11 +22,12 @@ export const Language = { signOutLabel: "Sign Out", } export interface UserDropdownProps { + isOpen?: boolean // *DO NOT USE* Only used for testing via Storybook user: TypesGen.User onSignOut: () => void } -export const UserDropdown: React.FC = ({ user, onSignOut }: UserDropdownProps) => { +export const UserDropdown: React.FC = ({ isOpen, user, onSignOut }: UserDropdownProps) => { const styles = useStyles() const [anchorEl, setAnchorEl] = useState() @@ -51,7 +52,7 @@ export const UserDropdown: React.FC = ({ user, onSignOut }: U = (args: UserProfileCardProps) => From 676de249856e13416e762d8c38494418002ffc9a Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Tue, 31 May 2022 20:56:31 +0000 Subject: [PATCH 3/8] add closed and open userdropdown tests --- site/src/components/UserDropdown/UserDropdown.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/components/UserDropdown/UserDropdown.stories.tsx b/site/src/components/UserDropdown/UserDropdown.stories.tsx index cce4ace4dddc4..eff2af37f96e9 100644 --- a/site/src/components/UserDropdown/UserDropdown.stories.tsx +++ b/site/src/components/UserDropdown/UserDropdown.stories.tsx @@ -17,8 +17,8 @@ const Template: Story = (args: UserDropdownProps) => ( ) -export const ExampleNoRoles = Template.bind({}) -ExampleNoRoles.args = { +export const Closed = Template.bind({}) +Closed.args = { user: MockUser, onSignOut: () => { return Promise.resolve() From fa407f96b088ee447935d8afadec95a818a54f48 Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Tue, 31 May 2022 21:54:47 +0000 Subject: [PATCH 4/8] add default isOpen --- site/src/components/UserDropdown/UsersDropdown.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/site/src/components/UserDropdown/UsersDropdown.tsx b/site/src/components/UserDropdown/UsersDropdown.tsx index 8a8bd310d5e9a..c1aa912543210 100644 --- a/site/src/components/UserDropdown/UsersDropdown.tsx +++ b/site/src/components/UserDropdown/UsersDropdown.tsx @@ -22,12 +22,15 @@ export const Language = { signOutLabel: "Sign Out", } export interface UserDropdownProps { - isOpen?: boolean // *DO NOT USE* Only used for testing via Storybook user: TypesGen.User + /** + * isOpen, defaults to false, only used for testing, to open the menu without clicking + */ + isOpen?: boolean onSignOut: () => void } -export const UserDropdown: React.FC = ({ isOpen, user, onSignOut }: UserDropdownProps) => { +export const UserDropdown: React.FC = ({ isOpen = false, user, onSignOut }: UserDropdownProps) => { const styles = useStyles() const [anchorEl, setAnchorEl] = useState() @@ -52,7 +55,7 @@ export const UserDropdown: React.FC = ({ isOpen, user, onSign Date: Wed, 1 Jun 2022 20:10:51 +0000 Subject: [PATCH 5/8] extract UserDropdownContent into a single component --- .../UserDropdown/UserDropdown.stories.tsx | 13 +- .../UserDropdown/UserDropdown.test.tsx | 57 +------ .../components/UserDropdown/UsersDropdown.tsx | 65 +------- .../UserDropdownContent.stories.tsx} | 8 +- .../UserDropdownContent.test.tsx | 61 +++++++ .../UserDropdownContent.tsx | 150 ++++++++++++++++++ .../UserProfileCard/UserProfileCard.tsx | 79 --------- 7 files changed, 222 insertions(+), 211 deletions(-) rename site/src/components/{UserProfileCard/UserProfileCard.stories.tsx => UserDropdownContent/UserDropdownContent.stories.tsx} (71%) create mode 100644 site/src/components/UserDropdownContent/UserDropdownContent.test.tsx create mode 100644 site/src/components/UserDropdownContent/UserDropdownContent.tsx delete mode 100644 site/src/components/UserProfileCard/UserProfileCard.tsx diff --git a/site/src/components/UserDropdown/UserDropdown.stories.tsx b/site/src/components/UserDropdown/UserDropdown.stories.tsx index eff2af37f96e9..6b76fa11e4b64 100644 --- a/site/src/components/UserDropdown/UserDropdown.stories.tsx +++ b/site/src/components/UserDropdown/UserDropdown.stories.tsx @@ -17,17 +17,8 @@ const Template: Story = (args: UserDropdownProps) => ( ) -export const Closed = Template.bind({}) -Closed.args = { - user: MockUser, - onSignOut: () => { - return Promise.resolve() - }, -} - -export const Open = Template.bind({}) -Open.args = { - isOpen: true, +export const Example = Template.bind({}) +Example.args = { user: MockUser, onSignOut: () => { return Promise.resolve() diff --git a/site/src/components/UserDropdown/UserDropdown.test.tsx b/site/src/components/UserDropdown/UserDropdown.test.tsx index 6ef615182cff8..6772100e97685 100644 --- a/site/src/components/UserDropdown/UserDropdown.test.tsx +++ b/site/src/components/UserDropdown/UserDropdown.test.tsx @@ -1,7 +1,8 @@ import { screen } from "@testing-library/react" -import { MockAdminRole, MockUser } from "../../testHelpers/entities" +import { MockUser } from "../../testHelpers/entities" import { render } from "../../testHelpers/renderHelpers" -import { Language, UserDropdown, UserDropdownProps } from "./UsersDropdown" +import { Language } from "../UserDropdownContent/UserDropdownContent" +import { UserDropdown, UserDropdownProps } from "./UsersDropdown" const renderAndClick = async (props: Partial = {}) => { render() @@ -10,18 +11,6 @@ const renderAndClick = async (props: Partial = {}) => { } describe("UserDropdown", () => { - const env = process.env - - // REMARK: copying process.env so we don't mutate that object or encounter conflicts between tests - beforeEach(() => { - process.env = { ...env } - }) - - // REMARK: restoring process.env - afterEach(() => { - process.env = env - }) - describe("when the trigger is clicked", () => { it("opens the menu", async () => { await renderAndClick() @@ -30,44 +19,4 @@ describe("UserDropdown", () => { expect(screen.getByText(Language.signOutLabel)).toBeDefined() }) }) - - describe("when the menu is open", () => { - it("displays the user's roles", async () => { - await renderAndClick() - - expect(screen.getByText(MockAdminRole.display_name)).toBeDefined() - }) - - it("has the correct link for the documentation item", async () => { - process.env.CODER_VERSION = "v0.5.4" - await renderAndClick() - - const link = screen.getByText(Language.docsLabel).closest("a") - if (!link) { - throw new Error("Anchor tag not found for the documentation menu item") - } - - expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`) - }) - - it("has the correct link for the account item", async () => { - await renderAndClick() - - const link = screen.getByText(Language.accountLabel).closest("a") - if (!link) { - throw new Error("Anchor tag not found for the account menu item") - } - - expect(link.getAttribute("href")).toBe("/settings/account") - }) - - describe("and sign out is clicked", () => { - it("calls the onSignOut function", async () => { - const onSignOut = jest.fn() - await renderAndClick({ onSignOut }) - screen.getByText(Language.signOutLabel).click() - expect(onSignOut).toBeCalledTimes(1) - }) - }) - }) }) diff --git a/site/src/components/UserDropdown/UsersDropdown.tsx b/site/src/components/UserDropdown/UsersDropdown.tsx index c1aa912543210..4490eab37a74a 100644 --- a/site/src/components/UserDropdown/UsersDropdown.tsx +++ b/site/src/components/UserDropdown/UsersDropdown.tsx @@ -1,26 +1,14 @@ import Badge from "@material-ui/core/Badge" -import Divider from "@material-ui/core/Divider" -import ListItemIcon from "@material-ui/core/ListItemIcon" -import ListItemText from "@material-ui/core/ListItemText" import MenuItem from "@material-ui/core/MenuItem" import { fade, makeStyles } from "@material-ui/core/styles" -import AccountIcon from "@material-ui/icons/AccountCircleOutlined" import React, { useState } from "react" -import { Link } from "react-router-dom" import * as TypesGen from "../../api/typesGenerated" import { navHeight } from "../../theme/constants" import { BorderedMenu } from "../BorderedMenu/BorderedMenu" import { CloseDropdown, OpenDropdown } from "../DropdownArrows/DropdownArrows" -import { DocsIcon } from "../Icons/DocsIcon" -import { LogoutIcon } from "../Icons/LogoutIcon" import { UserAvatar } from "../UserAvatar/UserAvatar" -import { UserProfileCard } from "../UserProfileCard/UserProfileCard" +import { UserDropdownContent } from "../UserDropdownContent/UserDropdownContent" -export const Language = { - accountLabel: "Account", - docsLabel: "Documentation", - signOutLabel: "Sign Out", -} export interface UserDropdownProps { user: TypesGen.User /** @@ -68,43 +56,7 @@ export const UserDropdown: React.FC = ({ isOpen = false, user variant="user-dropdown" onClose={onPopoverClose} > -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ) @@ -123,10 +75,6 @@ export const useStyles = makeStyles((theme) => ({ maxWidth: 300, }, - userInfo: { - marginBottom: theme.spacing(1), - }, - menuItem: { height: navHeight, padding: `${theme.spacing(1.5)}px ${theme.spacing(2.75)}px`, @@ -136,13 +84,4 @@ export const useStyles = makeStyles((theme) => ({ transition: "background-color 0.3s ease", }, }, - - link: { - textDecoration: "none", - color: "inherit", - }, - - icon: { - color: theme.palette.text.secondary, - }, })) diff --git a/site/src/components/UserProfileCard/UserProfileCard.stories.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.stories.tsx similarity index 71% rename from site/src/components/UserProfileCard/UserProfileCard.stories.tsx rename to site/src/components/UserDropdownContent/UserDropdownContent.stories.tsx index 024ee8114f290..e0ce3c1c60e95 100644 --- a/site/src/components/UserProfileCard/UserProfileCard.stories.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.stories.tsx @@ -1,15 +1,15 @@ import { Story } from "@storybook/react" import React from "react" import { MockUser } from "../../testHelpers/entities" -import { UserProfileCard, UserProfileCardProps } from "./UserProfileCard" +import { UserDropdownContent, UserDropdownContentProps } from "./UserDropdownContent" export default { - title: "components/UserProfileCard", - component: UserProfileCard, + title: "components/UserDropdownContent", + component: UserDropdownContent, argTypes: {}, } -const Template: Story = (args: UserProfileCardProps) => +const Template: Story = (args: UserDropdownContentProps) => export const ExampleNoRoles = Template.bind({}) ExampleNoRoles.args = { diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx new file mode 100644 index 0000000000000..ef2c939d62904 --- /dev/null +++ b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx @@ -0,0 +1,61 @@ +import { screen } from "@testing-library/react" +import { MockAdminRole, MockUser } from "../../testHelpers/entities" +import { render } from "../../testHelpers/renderHelpers" +import { Language, UserDropdownContent } from "./UserDropdownContent" + +describe("UserProfileCard", () => { + const env = process.env + + // REMARK: copying process.env so we don't mutate that object or encounter conflicts between tests + beforeEach(() => { + process.env = { ...env } + }) + + // REMARK: restoring process.env + afterEach(() => { + process.env = env + }) + + it("displays the menu items", () => { + render() + expect(screen.getByText(Language.accountLabel)).toBeDefined() + expect(screen.getByText(Language.docsLabel)).toBeDefined() + expect(screen.getByText(Language.signOutLabel)).toBeDefined() + }) + + it("displays the user's roles", () => { + render() + + expect(screen.getByText(MockAdminRole.display_name)).toBeDefined() + }) + + it("has the correct link for the documentation item", () => { + process.env.CODER_VERSION = "v0.5.4" + render() + + const link = screen.getByText(Language.docsLabel).closest("a") + if (!link) { + throw new Error("Anchor tag not found for the documentation menu item") + } + + expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`) + }) + + it("has the correct link for the account item", () => { + render() + + const link = screen.getByText(Language.accountLabel).closest("a") + if (!link) { + throw new Error("Anchor tag not found for the account menu item") + } + + expect(link.getAttribute("href")).toBe("/settings/account") + }) + + it("calls the onSignOut function", () => { + const onSignOut = jest.fn() + render() + screen.getByText(Language.signOutLabel).click() + expect(onSignOut).toBeCalledTimes(1) + }) +}) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.tsx new file mode 100644 index 0000000000000..d07a35888296a --- /dev/null +++ b/site/src/components/UserDropdownContent/UserDropdownContent.tsx @@ -0,0 +1,150 @@ +import Chip from "@material-ui/core/Chip" +import Divider from "@material-ui/core/Divider" +import ListItemIcon from "@material-ui/core/ListItemIcon" +import ListItemText from "@material-ui/core/ListItemText" +import MenuItem from "@material-ui/core/MenuItem" +import { fade, makeStyles } from "@material-ui/core/styles" +import Typography from "@material-ui/core/Typography" +import AccountIcon from "@material-ui/icons/AccountCircleOutlined" +import { FC } from "react" +import { Link } from "react-router-dom" +import * as TypesGen from "../../api/typesGenerated" +import { Role } from "../../api/typesGenerated" +import { navHeight } from "../../theme/constants" +import { DocsIcon } from "../Icons/DocsIcon" +import { LogoutIcon } from "../Icons/LogoutIcon" +import { UserAvatar } from "../UserAvatar/UserAvatar" + +export const Language = { + accountLabel: "Account", + docsLabel: "Documentation", + signOutLabel: "Sign Out", +} +export interface UserDropdownContentProps { + user: TypesGen.User + onPopoverClose: () => void + onSignOut: () => void +} + +export const UserDropdownContent: FC = ({ user, onPopoverClose, onSignOut }) => { + const styles = useStyles() + + return ( +
+
+
+ +
+ {user.username} + {user.email} +
    + {user.roles.map((role: Role) => ( +
  • + +
  • + ))} +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ) +} + +const useStyles = makeStyles((theme) => ({ + root: { + paddingTop: theme.spacing(3), + textAlign: "center", + }, + avatarContainer: { + width: "100%", + display: "flex", + alignItems: "center", + justifyContent: "center", + }, + avatar: { + width: 48, + height: 48, + borderRadius: "50%", + marginBottom: theme.spacing(1), + transition: `transform .2s`, + + "&:hover": { + transform: `scale(1.1)`, + }, + }, + userName: { + fontSize: 16, + marginBottom: theme.spacing(0.5), + }, + userEmail: { + fontSize: 14, + letterSpacing: 0.2, + color: theme.palette.text.secondary, + }, + chipContainer: { + display: "flex", + justifyContent: "center", + flexWrap: "wrap", + listStyle: "none", + margin: "0", + padding: `${theme.spacing(1.5)}px ${theme.spacing(2.75)}px`, + }, + chipStyles: { + margin: theme.spacing(0.5), + }, + chipRoot: { + backgroundColor: "#7057FF", + }, + link: { + textDecoration: "none", + color: "inherit", + }, + menuItem: { + height: navHeight, + padding: `${theme.spacing(1.5)}px ${theme.spacing(2.75)}px`, + + "&:hover": { + backgroundColor: fade(theme.palette.primary.light, 0.1), + transition: "background-color 0.3s ease", + }, + }, + userInfo: { + marginBottom: theme.spacing(1), + }, + icon: { + color: theme.palette.text.secondary, + }, +})) diff --git a/site/src/components/UserProfileCard/UserProfileCard.tsx b/site/src/components/UserProfileCard/UserProfileCard.tsx deleted file mode 100644 index d171ab4b3951d..0000000000000 --- a/site/src/components/UserProfileCard/UserProfileCard.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import Chip from "@material-ui/core/Chip" -import { makeStyles } from "@material-ui/core/styles" -import Typography from "@material-ui/core/Typography" -import { FC } from "react" -import * as TypesGen from "../../api/typesGenerated" -import { Role } from "../../api/typesGenerated" -import { UserAvatar } from "../UserAvatar/UserAvatar" - -export interface UserProfileCardProps { - user: TypesGen.User -} - -export const UserProfileCard: FC = ({ user }) => { - const styles = useStyles() - - return ( -
-
- -
- {user.username} - {user.email} -
    - {user.roles.map((role: Role) => ( -
  • - -
  • - ))} -
-
- ) -} - -const useStyles = makeStyles((theme) => ({ - root: { - paddingTop: theme.spacing(3), - textAlign: "center", - }, - avatarContainer: { - width: "100%", - display: "flex", - alignItems: "center", - justifyContent: "center", - }, - avatar: { - width: 48, - height: 48, - borderRadius: "50%", - marginBottom: theme.spacing(1), - transition: `transform .2s`, - - "&:hover": { - transform: `scale(1.1)`, - }, - }, - userName: { - fontSize: 16, - marginBottom: theme.spacing(0.5), - }, - userEmail: { - fontSize: 14, - letterSpacing: 0.2, - color: theme.palette.text.secondary, - }, - chipContainer: { - display: "flex", - justifyContent: "center", - flexWrap: "wrap", - listStyle: "none", - margin: "0", - padding: `${theme.spacing(1.5)}px ${theme.spacing(2.75)}px`, - }, - chipStyles: { - margin: theme.spacing(0.5), - }, - chipRoot: { - backgroundColor: "#7057FF", - }, -})) From d54a791b86e05f4f76917c23337cc07164785e56 Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Wed, 1 Jun 2022 20:15:18 +0000 Subject: [PATCH 6/8] remove the isOpen prop --- site/src/components/UserDropdown/UsersDropdown.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/site/src/components/UserDropdown/UsersDropdown.tsx b/site/src/components/UserDropdown/UsersDropdown.tsx index 4490eab37a74a..7cca88e117c8a 100644 --- a/site/src/components/UserDropdown/UsersDropdown.tsx +++ b/site/src/components/UserDropdown/UsersDropdown.tsx @@ -11,14 +11,10 @@ import { UserDropdownContent } from "../UserDropdownContent/UserDropdownContent" export interface UserDropdownProps { user: TypesGen.User - /** - * isOpen, defaults to false, only used for testing, to open the menu without clicking - */ - isOpen?: boolean onSignOut: () => void } -export const UserDropdown: React.FC = ({ isOpen = false, user, onSignOut }: UserDropdownProps) => { +export const UserDropdown: React.FC = ({ user, onSignOut }: UserDropdownProps) => { const styles = useStyles() const [anchorEl, setAnchorEl] = useState() @@ -43,7 +39,7 @@ export const UserDropdown: React.FC = ({ isOpen = false, user Date: Thu, 2 Jun 2022 14:31:13 +0000 Subject: [PATCH 7/8] address nit comments --- .../UserDropdownContent/UserDropdownContent.stories.tsx | 3 +-- .../src/components/UserDropdownContent/UserDropdownContent.tsx | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.stories.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.stories.tsx index e0ce3c1c60e95..bc09586d17ffa 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.stories.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.stories.tsx @@ -6,10 +6,9 @@ import { UserDropdownContent, UserDropdownContentProps } from "./UserDropdownCon export default { title: "components/UserDropdownContent", component: UserDropdownContent, - argTypes: {}, } -const Template: Story = (args: UserDropdownContentProps) => +const Template: Story = (args) => export const ExampleNoRoles = Template.bind({}) ExampleNoRoles.args = { diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.tsx index d07a35888296a..4325631aecc19 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.tsx @@ -20,6 +20,7 @@ export const Language = { docsLabel: "Documentation", signOutLabel: "Sign Out", } + export interface UserDropdownContentProps { user: TypesGen.User onPopoverClose: () => void From 184073f4c12187c967e94aeb33b2bf0444ee7096 Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Thu, 2 Jun 2022 14:43:19 +0000 Subject: [PATCH 8/8] update test name --- .../components/UserDropdownContent/UserDropdownContent.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx index ef2c939d62904..53b6c2bb7b905 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx @@ -3,7 +3,7 @@ import { MockAdminRole, MockUser } from "../../testHelpers/entities" import { render } from "../../testHelpers/renderHelpers" import { Language, UserDropdownContent } from "./UserDropdownContent" -describe("UserProfileCard", () => { +describe("UserDropdownContent", () => { const env = process.env // REMARK: copying process.env so we don't mutate that object or encounter conflicts between tests