integration/reporting/disputing.test.ts
import "jest-environment-puppeteer";
import Flash from "../helpers/flash";
import { IFlash, IMarket, Outcome } from "../types/types";
import { UnlockedAccounts } from "../constants/accounts";
import { toDisputing } from "../helpers/navigation-helper";
import {
createYesNoMarket,
createCategoricalMarket,
createScalarMarket
} from "../helpers/create-markets";
import { waitNextBlock } from "../helpers/wait-new-block";
require("../helpers/beforeAll");
// TODO: Replace uses of `url` with calls to functions in navigation-helper
const url = `${process.env.AUGUR_URL}`;
const SMALL_TIMEOUT = 80000;
const BIG_TIMEOUT = 160000;
jest.setTimeout(200000);
let flash: IFlash = new Flash();
const disputeOnAllOutcomes = async (
marketId: string,
outcomes: Outcome[],
isScalar: boolean = false
) => {
const amount = "0.3000";
for (let i = 0; i < outcomes.length; i++) {
if (!outcomes[i].tentativeWinning) {
await disputeOnOutcome(marketId, outcomes[i], amount);
await verifyDisputedOutcome(marketId, outcomes[i].id, amount);
}
}
if (isScalar) {
const values: string[] = ["-2", ".2", "-.2", "10", "-10"];
for (const value of values) {
await disputeOnScalarOutcome(marketId, value, amount);
}
}
return;
};
const disputeOnOutcome = async (
marketId: string,
outcome: Outcome,
amount: string
) => {
await expect(page).toClick("[data-testid='link-" + marketId + "']", {
text: "dispute",
timeout: BIG_TIMEOUT
});
await expect(page).toClick("[data-testid='button-" + outcome.id + "']", {
timeout: BIG_TIMEOUT
});
await expect(page).toFill("#sr__input--stake", amount, {
timeout: SMALL_TIMEOUT
});
await expect(page).toClick("button", {
text: "Review",
timeout: SMALL_TIMEOUT
});
await expect(page).toClick("button", {
text: "Submit",
timeout: BIG_TIMEOUT
});
return;
};
const disputeOnScalarOutcome = async (
marketId: string,
outcomeValue: string,
amount: string
) => {
await expect(page).toClick("[data-testid='link-" + marketId + "']", {
text: "dispute",
timeout: BIG_TIMEOUT
});
await expect(page).toClick("[data-testid='scalar-dispute-button']", {
timeout: BIG_TIMEOUT
});
await expect(page).toFill("#sr__input--outcome-scalar", outcomeValue, {
timeout: SMALL_TIMEOUT
});
await expect(page).toFill("#sr__input--stake", amount, {
timeout: SMALL_TIMEOUT
});
await expect(page).toClick("button", {
text: "Review",
timeout: SMALL_TIMEOUT
});
await expect(page).toClick("button", {
text: "Submit",
timeout: BIG_TIMEOUT
});
return;
};
const verifyDisputedOutcome = async (
marketId: string,
outcomeId: string,
amount: string
) => {
// TODO: need to be aware of "+ more" button
await expect(page).toMatchElement(
"[data-testid='disputeBond-" + marketId + "-" + outcomeId + "']",
{
text: amount,
timeout: BIG_TIMEOUT
}
);
return;
};
describe("Disputing", () => {
let market: IMarket;
beforeAll(async () => {
await toDisputing();
market = await createYesNoMarket();
await waitNextBlock(10);
await flash.initialReport(market.id, "0", false, false);
await flash.pushWeeks(1); // push into dispute window
});
afterAll(async () => {
flash.dispose();
});
describe("Basics", () => {
it("should be shown the 'No-REP' message if your account has no REP", async () => {
await page.evaluate(
account => window.integrationHelpers.updateAccountAddress(account),
UnlockedAccounts.SECONDARY_ACCOUNT
);
await toDisputing();
await expect(page).toMatch(
"You have 0 REP available. Add funds to dispute markets or purchase participation tokens.",
{ timeout: SMALL_TIMEOUT }
);
});
it("should not be able to submit a dispute without REP", async () => {
// check that button is disabled
await expect(page).toMatchElement(
"[data-testid='link-" + market.id + "']",
{ text: "dispute", timeout: SMALL_TIMEOUT }
);
});
});
describe("Disputing Mechanisms", () => {
let yesNoMarket: IMarket;
let categoricalMarket: IMarket;
let scalarMarket: IMarket;
let outcomes: { [key: string]: Outcome[] };
beforeAll(async () => {
await page.evaluate(() => window.integrationHelpers.getRep());
await waitNextBlock(2);
});
describe("Yes/No Market", () => {
beforeAll(async () => {
yesNoMarket = await createYesNoMarket();
await waitNextBlock(15);
await flash.initialReport(yesNoMarket.id, "0", false, false);
await flash.pushWeeks(1);
await waitNextBlock(15);
outcomes = await page.evaluate(() =>
window.integrationHelpers.getMarketDisputeOutcomes()
);
});
it("should be able to dispute on all outcomes", async () => {
await disputeOnAllOutcomes(
yesNoMarket.id,
outcomes[yesNoMarket.id],
false
);
});
});
describe("Categorical Market", () => {
beforeAll(async () => {
categoricalMarket = await createCategoricalMarket(4);
await waitNextBlock(15);
await flash.initialReport(categoricalMarket.id, "0", false, false);
await flash.pushWeeks(1);
await waitNextBlock(15);
outcomes = await page.evaluate(() =>
window.integrationHelpers.getMarketDisputeOutcomes()
);
});
it("should be able to dispute on all outcomes", async () => {
await disputeOnAllOutcomes(
categoricalMarket.id,
outcomes[categoricalMarket.id],
false
);
});
});
describe("Scalar Market", () => {
beforeAll(async () => {
scalarMarket = await createScalarMarket();
await waitNextBlock(15);
await flash.initialReport(scalarMarket.id, "0", false, false);
await flash.pushWeeks(1);
await waitNextBlock(15);
outcomes = await page.evaluate(() =>
window.integrationHelpers.getMarketDisputeOutcomes()
);
});
it("should be able to dispute on all outcomes", async () => {
await disputeOnAllOutcomes(
scalarMarket.id,
outcomes[scalarMarket.id],
true
);
});
});
});
describe("Dispute Window", () => {
let daysLeft: number;
let reportingWindowStats;
it("should have days remaining be correct", async () => {
await toDisputing();
reportingWindowStats = await page.evaluate(() =>
window.integrationHelpers.getReportingWindowStats()
);
let currentTimestamp = await page.evaluate(() =>
window.integrationHelpers.getCurrentTimestamp()
);
currentTimestamp = currentTimestamp / 1000;
daysLeft = await page.evaluate(
(endTime, startTime) =>
window.integrationHelpers.getDaysRemaining(endTime, startTime),
reportingWindowStats.endTime,
currentTimestamp
);
let denomination = daysLeft === 1 ? " day" : " days";
if (daysLeft === 0) {
const hoursLeft = await page.evaluate(
(endTime, startTime) =>
window.integrationHelpers.getHoursRemaining(endTime, startTime),
reportingWindowStats.endTime,
currentTimestamp
);
denomination = hoursLeft === 1 ? " hour" : " hours";
if (hoursLeft === 0) {
const minutesLeft = await page.evaluate(
(endTime, startTime) =>
window.integrationHelpers.getMinutesRemaining(endTime, startTime),
reportingWindowStats.endTime,
currentTimestamp
);
denomination = minutesLeft === 1 ? " minute" : " minutes";
// check that days left is expected number
await expect(page).toMatchElement("[data-testid='daysLeft']", {
text: minutesLeft + denomination + " left",
timeout: SMALL_TIMEOUT
});
} else {
// check that days left is expected number
await expect(page).toMatchElement("[data-testid='daysLeft']", {
text: hoursLeft + denomination + " left",
timeout: SMALL_TIMEOUT
});
}
} else {
// check that days left is expected number
await expect(page).toMatchElement("[data-testid='daysLeft']", {
text: daysLeft + denomination + " left",
timeout: SMALL_TIMEOUT
});
}
});
it("should have days remaining increment properly", async () => {
// push time
await flash.pushDays(1);
let daysLeftIncr = daysLeft === 0 ? 6 : daysLeft - 1;
let denomination = daysLeftIncr === 1 ? " day" : " days";
if (daysLeftIncr === 0) {
let currentTimestamp = await page.evaluate(() =>
window.integrationHelpers.getCurrentTimestamp()
);
currentTimestamp = currentTimestamp / 1000;
daysLeftIncr = await page.evaluate(
(endTime, startTime) =>
window.integrationHelpers.getHoursRemaining(endTime, startTime),
reportingWindowStats.endTime,
currentTimestamp
);
denomination = daysLeftIncr === 1 ? " hour" : " hours";
if (daysLeftIncr === 0) {
daysLeftIncr = await page.evaluate(
(endTime, startTime) =>
window.integrationHelpers.getMinutesRemaining(endTime, startTime),
reportingWindowStats.endTime,
currentTimestamp
);
denomination = daysLeftIncr === 1 ? " minute" : " minutes";
}
} else if (daysLeftIncr === 6) {
daysLeftIncr = 0;
denomination = " minutes";
}
// check that days left is previous calculation - time pushed
await expect(page).toMatchElement("[data-testid='daysLeft']", {
text: daysLeftIncr + denomination + " left",
timeout: SMALL_TIMEOUT
});
});
it("should have correct end date", async () => {
// get new stats because endTime could be different because of time push
reportingWindowStats = await page.evaluate(() =>
window.integrationHelpers.getReportingWindowStats()
);
const formattedDate = await page.evaluate(
date => window.integrationHelpers.convertUnixToFormattedDate(date),
reportingWindowStats.endTime
);
// check that dispute window ends is displayed correctly
await expect(page).toMatchElement("[data-testid='endTime']", {
text: formattedDate.clockTimeLocal,
timeout: BIG_TIMEOUT
});
});
it("should update correctly when time is pushed and a new dispute window starts", async () => {
// push into new dispute window
await flash.pushDays(7);
// get new stats
reportingWindowStats = await page.evaluate(() =>
window.integrationHelpers.getReportingWindowStats()
);
const formattedDate = await page.evaluate(
date => window.integrationHelpers.convertUnixToFormattedDate(date),
reportingWindowStats.endTime
);
// check that dispute window ends is displayed correctly
await expect(page).toMatchElement("[data-testid='endTime']", {
text: formattedDate.clockTimeLocal,
timeout: BIG_TIMEOUT
});
});
it("should create a new dispute window properly even when no markets were reported on or disputed in the previous dispute window", async () => {});
});
describe("Market Card", () => {
let market: IMarket;
describe("Dispute Bonds", () => {
it("should have all of the dispute bonds on a market be equal to one another in the first dispute round", async () => {
// create new yes/no market
market = await createYesNoMarket();
await waitNextBlock(10);
// put yes/no market into disputing
await flash.initialReport(market.id, "0", false, false);
await waitNextBlock(2);
// check that dispute bonds for outcomes yes and market is invalid are expected
// TODO: make .6994 not hard coded, and make this reusable for different market types -- use outcomes selector
await expect(page).toMatchElement(
"[data-testid='disputeBondTarget-" + market.id + "-1']",
{
text: "0.6994 REP",
timeout: BIG_TIMEOUT
}
);
await expect(page).toMatchElement(
"[data-testid='disputeBondTarget-" + market.id + "-0.5']",
{
text: "0.6994 REP",
timeout: BIG_TIMEOUT
}
);
});
it("should have dispute bonds be equal to twice the amount placed by the initial reporter in the first dispute round", async () => {
// TODO:
// With markets reported on by the Designated Reporter, this is twice the stake placed by the Designated Reporter.
// With markets reported on in Open Reporting, this is twice the no-show bond.
// Test both.
});
});
describe("Round Numbers", () => {
it("should have round number be 1 while a market is waiting for its first Dispute window and while in its first round number", async () => {
await expect(page).toMatchElement(
"[data-testid='roundNumber-" + market.id + "']",
{
text: "1",
timeout: SMALL_TIMEOUT
}
);
});
it("should have round number increase if a dispute is successful and a market is waiting for or is in its next dispute window", async () => {
await flash.disputeContribute(market.id, "1", false, false);
await expect(page).toMatchElement(
"[data-testid='roundNumber-" + market.id + "']",
{
text: "2",
timeout: SMALL_TIMEOUT
}
);
});
});
describe("Listed Outcomes", () => {
describe("Yes/No Market", () => {
let yesNoMarket: IMarket;
it("should have the market's reported-on outcome display correctly on the market card", async () => {
yesNoMarket = await createYesNoMarket();
await waitNextBlock(10);
await flash.initialReport(yesNoMarket.id, "0", false, false);
await flash.pushWeeks(1);
await waitNextBlock(2);
// expect not to have a dispute bond for winning outcome
await expect(page).not.toMatchElement(
"[data-testid='disputeBondTarget-" + yesNoMarket.id + "-0']"
);
});
it("should have 'Yes', 'No', and 'Market is Invalid' outcomes be present", async () => {
await expect(page).toMatch("Yes");
await expect(page).toMatch("Invalid");
await expect(page).toMatch("No");
});
});
describe("Categorical Market", () => {
it("should have the market's reported-on outcome display correctly on the market card", async () => {
const categoricalMarket = await createCategoricalMarket(4);
await waitNextBlock(10);
await flash.initialReport(categoricalMarket.id, "0", false, false);
await flash.pushWeeks(1);
await waitNextBlock(2);
// expect not to have a dispute bond for winning outcome
await expect(page).not.toMatchElement(
"[data-testid='disputeBondTarget-" + categoricalMarket.id + "-0']"
);
});
});
describe("Scalar Market", () => {
it("should have the market's reported-on outcome display correctly on the market card", async () => {
const scalarMarket = await createScalarMarket();
await waitNextBlock(10);
await flash.initialReport(scalarMarket.id, "1", false, false);
await waitNextBlock(2);
await flash.pushWeeks(1);
await expect(page).toMatch("Invalid", { timeout: SMALL_TIMEOUT });
// expect not to have a dispute bond for winning outcome
await expect(page).toMatchElement(
"[data-testid='winning-" + scalarMarket.id + "-1']"
);
});
it("should have no other outcomes listed when the tentative winning outcome is 'Market is Invalid'", async () => {
const scalarMarket = await createScalarMarket();
await waitNextBlock(10);
await flash.initialReport(scalarMarket.id, "0", true, false);
await waitNextBlock(2);
await flash.pushWeeks(1);
await expect(page).toMatch("Invalid", { timeout: SMALL_TIMEOUT });
// expect not to have a dispute bond for winning outcome
await expect(page).not.toMatchElement(
"[data-testid='disputeBondTarget-" + scalarMarket.id + "-0']"
);
});
});
});
});
});