AugurProject/augur-ui

View on GitHub
integration/reporting/disputing.test.ts

Summary

Maintainability
D
2 days
Test Coverage
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']"
          );
        });
      });
    });
  });
});