Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests to tips example #833

Merged
merged 15 commits into from
Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 133 additions & 3 deletions .github/workflows/cypress-end-to-end-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,7 @@ jobs:
node-version: 16

- name: 🤖 Install Mephisto
run: |
pip install -e .
pip install detoxify
run: pip install -e .

- name: 🖋 Create data directory
run: |
Expand Down Expand Up @@ -194,3 +192,135 @@ jobs:
wait-on: "http://localhost:3000/?worker_id=x&assignment_id=1"
command-prefix: yarn dlx
headless: true

static_react_task_with_tips:
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- name: 🔀 Checking out repo
uses: actions/checkout@v2
- name: 🐍 Installing python
uses: actions/setup-python@v2

- name: 🪨 Setup Node
uses: actions/setup-node@v1
with:
node-version: 16

- name: 🤖 Install Mephisto
run: |
pip install -e .
pip uninstall detoxify <<EOF
y
EOF
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protip: there's a command called yes that you can use here instead:

yes | pip uninstall detoxify

NAME
     yes – be repetitively affirmative

SYNOPSIS
     yes [expletive]

DESCRIPTION
     The yes utility outputs expletive, or, by default, “y”, forever.


- name: 🖋 Create data directory
run: |
cd ~
mkdir mephisto
cd mephisto
mkdir data

- name: 📂 Set the data directory
run: mephisto config core.main_data_directory ~/mephisto/data

- name: 📦 Setting up mephisto-task package
run: |
cd packages/mephisto-task
yarn install
yarn build
npm link

- name: 📦 Setting up mephisto-worker-addons package
run: |
cd packages/mephisto-worker-addons
yarn install
yarn build
npm link

- name: ⌛️ Running pre-submission cypress tests
uses: cypress-io/[email protected]
with:
install: false
browser: chrome
project: ./examples/static_react_task_with_tips/webapp
config-file: ./cypress.config.js
spec: ./examples/static_react_task_with_tips/webapp/cypress/e2e/pre_submission_tests/*
start: python examples/static_react_task_with_tips/run_task.py mephisto.task.force_rebuild=true mephisto.task.post_install_script=link_packages.sh
wait-on: "http://localhost:3000/?worker_id=x&assignment_id=1"
command-prefix: yarn dlx
headless: true

- name: 🔪 Killing the web server
run: |
lsof -nPi :3000
lsof -i -P -n | grep LISTEN | grep python | awk '{print $2}'
kill -INT $(lsof -i -P -n | grep LISTEN | grep python | awk '{print $2}')
echo "killed 1"
sleep 0.5
kill -INT $(lsof -i -P -n | grep LISTEN | grep python | awk '{print $2}')
echo "killed 2"
lsof -i -P -n | grep LISTEN | grep node | awk '{print $2}'
kill -INT $(lsof -i -P -n | grep LISTEN | grep node | awk '{print $2}')
sleep 30
echo "30 seconds passed"

- name: 🥛 Expiring units
run: |
cd mephisto/scripts/local_db
python expire_all_units.py

- name: 📚 Accepting the first submitted tip, accepting the second submitted tip, and rejecting the last submitted tip
run: |
cd mephisto/scripts/local_db
python review_tips_for_task.py << EOF
react-static-task-with-tips
a
5
The tip is very informative
a
0
r
EOF

- name: 🔥 Removing the second accepted tip
run: |
cd mephisto/scripts/local_db
python remove_accepted_tip.py << EOF
react-static-task-with-tips
n
y
EOF

- name: 📖 Reviewing the accepted feedback
run: |
cd mephisto/scripts/local_db
python review_feedback_for_task.py << EOF
react-static-task-with-tips
0
n
u
y
y
EOF
python review_feedback_for_task.py << EOF
react-static-task-with-tips
1
n
u
y
n
EOF

- name: ⌛️ Running post-submission cypress tests
uses: cypress-io/[email protected]
with:
install: false
project: ./examples/static_react_task_with_tips/webapp
config-file: cypress.config.js
spec: ./examples/static_react_task_with_tips/webapp/cypress/e2e/post_submission_tests/*
start: python examples/static_react_task_with_tips/run_task.py mephisto.task.post_install_script=link_packages.sh mephisto.task.force_rebuild=true
wait-on: "http://localhost:3000/?worker_id=x&assignment_id=1"
command-prefix: yarn dlx
browser: chrome
headless: true
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ describe("Loads remote_procedure_toxicity_detection", () => {
cy.get("@textArea").type("I hate bob!");
cy.get("@submitButton").click();
cy.get('[data-cy="loading-spinner"]');
// This timeout is 25000 because the detoxify model takes a good bit of time to run
cy.get('[data-cy="toxicity-alert"]', { timeout: 25000 }).as(
// This timeout is 40000 because the detoxify model takes a good bit of time to run
cy.get('[data-cy="toxicity-alert"]', { timeout: 40000 }).as(
"toxicityAlert"
);
cy.get("@toxicityAlert").contains(
Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,3 @@ mephisto:
task_tags: "test,simple,button,tips"
# We expect to be able to handle 300 concurrent tasks without issue
max_num_concurrent_units: 300
force_rebuild: true
post_install_script: "link_packages.sh"
8 changes: 8 additions & 0 deletions examples/static_react_task_with_tips/webapp/cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
video: false,

e2e: {
baseUrl: "http://localhost:3000/?worker_id=x&assignment_id=1",
includeShadowDom: true,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { tipClassNamePrefix } from "../../helper";
describe("Checking for tips", () => {
/*
This test is supposed to be ran after running the pre_tip_submission cypress test.
The pre_tip_submission cypress test submits three tips for reviewing.
Before running this test, you should review the tips for static_react_task_with_tips
by running python review_tips_for_task.py.

To pass this test, accept the first tip and reject the other two.
Then run this test and it should pass.

The whole process is gone through in the cypress-end-to-end-tests.yml github actions file.
*/
it("Checks for recently added tip", () => {
cy.visit("/");
cy.get(`.${tipClassNamePrefix}button`).as("tipsButton");
cy.get("@tipsButton").click();

cy.get(`.${tipClassNamePrefix}tip`).eq(-1).as("lastTip");

cy.get("@lastTip").find("h2").as("lastTipHeader");
cy.get("@lastTip").find("p").as("lastTipBody");

cy.get("@lastTipHeader").should(
"have.text",
"🎉 This is my test tip header"
);
cy.get("@lastTipBody").should("have.text", "🎈 This is my test tip body");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { tipClassNamePrefix } from "../../helper";

const firstTipHeader = "🎉 This is my test tip header";
const firstTipBody = "🎈 This is my test tip body";

const secondTipHeader = "This is my second tip header";
const secondTipBody = "This is my second tip body";

const thirdTipHeader = "🐍 🦀 🦑";
const thirdTipBody = "🥨 🍗 🍟";

const headerError = "This header is toooooooooooooooooooooooooooooooooo long";
const bodyError =
"This body is toooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo long";

describe("Loads static_react_task_with_tips", () => {
it("Makes request for agent", () => {
cy.intercept({ pathname: "/request_agent" }).as("agentRequest");
cy.visit("/");
cy.wait("@agentRequest").then((interception) => {
expect(interception.response.statusCode).to.eq(200);
});
});

it("Loads correct task react elements", () => {
cy.visit("/");
cy.get('[data-cy="directions-container"]');
cy.get('[data-cy="task-data-text"]');
cy.get('[data-cy="good-button"]');
cy.get('[data-cy="bad-button"]');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think these could be suffixed with .should('exist') for readability and clearer assertions. this also bakes in some retry semantics...

another option is .should('be.visible') if we want to also check for cases where the element is visible (and not being pushed off screen or hidden by CSS)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that said, i realize that .get by itself does error out if the element isn't found, so perhaps it's not as necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't think it is necessary. I mainly use cy.should('not.exist') to check for content that is not on the page, but I don't really know the point of using cy.should('exist')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it seems it's redundant, it maybe just indicates intention that an assertion is taking place. i'm fine with keeping it as is

});

it("Loads correct tip react elements", () => {
cy.visit("/");
cy.get(`.${tipClassNamePrefix}button`);
});
});

describe("Tips Popup", () => {
it("Opening/Closing tips popup", () => {
cy.visit("/");
cy.get(`.${tipClassNamePrefix}button`).as("tipsButton");
cy.get("@tipsButton").click();

cy.get(`.${tipClassNamePrefix}container`).as("tipsContainer");
cy.get("@tipsContainer").should("exist");
cy.get("h1").contains("Task Tips:");
cy.get("h1").contains("Submit A Tip:");
cy.get("label").contains("Tip Headline:");
cy.get("label").contains("Tip Body:");
cy.get(`.${tipClassNamePrefix}button`).should("be.disabled");

// Closing popup by clicking the hide tips button
cy.get("@tipsButton").click();
cy.get("@tipsContainer").should("not.exist");

cy.get("@tipsButton").click();
cy.get("@tipsContainer").should("exist");
cy.get("h1").contains("Task Tips:");
cy.get("h1").contains("Submit A Tip:");
cy.get("label").contains("Tip Headline:");
cy.get("label").contains("Tip Body:");
cy.get(`.${tipClassNamePrefix}button`).should("be.disabled");

// Closing popup by clicking close button
cy.get(`.${tipClassNamePrefix}close-icon-container`).click();
cy.get("@tipsContainer").should("not.exist");
});

it("Checking if tips header is too long", () => {
cy.visit("/");
cy.get(`.${tipClassNamePrefix}button`).as("tipsButton");
cy.get("@tipsButton").click();

cy.get(`#${tipClassNamePrefix}header-input`).as("tipsHeaderInput");
cy.get("@tipsHeaderInput").type(headerError);
cy.get(`.${tipClassNamePrefix}red-box`).should(
"have.text",
"📝 Your tip header is too long"
);
cy.get(`.${tipClassNamePrefix}button`).should("be.disabled");

/*
There needs to be {force:true} in the clear because otherwise there is a
"cy.type() failed because it targeted a disabled element" error

These issues are both related to it, there seems to be no clear solution:
https:/cypress-io/cypress/issues/5830
https:/cypress-io/cypress/issues/21433

*/
cy.get("@tipsHeaderInput").clear({ force: true });
cy.get(`.${tipClassNamePrefix}red-box`).should("not.exist");
});

it("Checking if tips body is too long", () => {
cy.visit("/");
cy.get(`.${tipClassNamePrefix}button`).as("tipsButton");
cy.get("@tipsButton").click();

cy.get(`#${tipClassNamePrefix}text-input`).as("tipsBodyInput");
cy.get("@tipsBodyInput").type(bodyError);
cy.get(`.${tipClassNamePrefix}red-box`).should(
"have.text",
"📝 Your tip body is too long"
);
cy.get(`.${tipClassNamePrefix}button`).should("be.disabled");
cy.get("@tipsBodyInput").clear({ force: true });
cy.get(`.${tipClassNamePrefix}red-box`).should("not.exist");
});

it("Checking if both tips header and tips body is too long", () => {
cy.visit("/");
cy.get(`.${tipClassNamePrefix}button`).as("tipsButton");
cy.get("@tipsButton").click();

cy.get(`#${tipClassNamePrefix}header-input`).as("tipsHeaderInput");
cy.get("@tipsHeaderInput").should("not.be.disabled");
cy.get("@tipsHeaderInput").type(headerError);
cy.get(`.${tipClassNamePrefix}red-box`).should(
"have.text",
"📝 Your tip header is too long"
);
cy.get(`.${tipClassNamePrefix}button`).should("be.disabled");

cy.get(`#${tipClassNamePrefix}text-input`).as("tipsBodyInput");
cy.get("@tipsBodyInput").should("not.be.disabled");
cy.get("@tipsBodyInput").type(bodyError);

cy.get(`.${tipClassNamePrefix}button`).should("be.disabled");
cy.get(`.${tipClassNamePrefix}red-box`).should(
"have.text",
"📝 Your tip header is too long"
);
cy.get("@tipsHeaderInput").clear({ force: true });
cy.get(`.${tipClassNamePrefix}red-box`).should(
"have.text",
"📝 Your tip body is too long"
);
cy.get(`.${tipClassNamePrefix}button`).should("be.disabled");
cy.get("@tipsBodyInput").clear({ force: true });
cy.get(`.${tipClassNamePrefix}red-box`).should("not.exist");
});

it("Submitting three tips", () => {
cy.intercept({ pathname: "/submit_metadata" }).as("submitMetadataRequest");
cy.visit("/");
cy.get(`.${tipClassNamePrefix}button`)
.contains("Show Tips")
.as("tipsButton");
cy.get("@tipsButton").click();
cy.get(`.${tipClassNamePrefix}button`)
.contains("Submit Tip")
.as("submitButton");
cy.get("@submitButton").should("be.disabled");
cy.get(`#${tipClassNamePrefix}header-input`).as("tipsHeaderInput");

cy.get(`#${tipClassNamePrefix}text-input`).as("tipsBodyInput");
cy.submitTip(firstTipHeader, firstTipBody);
cy.submitTip(secondTipHeader, secondTipBody);
cy.submitTip(thirdTipHeader, thirdTipBody);
});
});
Loading