Exemple #1
0
def test_clear_skipped_votes(
    *,
    category_name: Optional[str],
    commit: bool,
    conference: Conference,
    ending_count: int,
    user: User,
) -> None:
    """Test that skipped votes can be cleared for a uesr."""
    talk1 = Talk(title="", length=1, is_anonymized=True)
    talk2 = Talk(title="", length=1, is_anonymized=True)
    category1 = Category(conference=conference, name="First Category")
    category2 = Category(conference=conference, name="Second Category")
    vote1 = Vote(talk=talk1, user=user, skipped=True)
    vote2 = Vote(talk=talk2, user=user, skipped=True)
    category1.talks.append(talk1)
    category2.talks.append(talk2)
    db.session.add_all((category1, category2, talk1, talk2, vote1, vote2))
    db.session.commit()

    # Choose the category to delete skipped votes from based on the
    # provided category name.
    if category_name is None:
        target_category = None
    else:
        target_category = Category.query.filter_by(name=category_name).first()

    # Clear the skipped votes as configured.
    Vote.clear_skipped(category=target_category, commit=commit, user=user)

    # Rollback the session to clear any uncommitted changes.
    db.session.rollback()

    # Check for the desired skipped vote count.
    assert Vote.query.count() == ending_count
Exemple #2
0
def test_inviting_a_speaker_emails_the_speaker(client: Client, user: User,
                                               send_mail: Mock) -> None:
    talk = Talk(title="My Talk", length=25)
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(talk)
    db.session.commit()

    assert User.query.filter_by(
        email="*****@*****.**").one_or_none() is None

    # reload from DB to avoid "not attached to session" error
    talk = Talk.query.filter_by(title="My Talk").one()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks/{}/speakers".format(talk.talk_id))
    assert_html_response(resp)
    csrf_token = extract_csrf_from(resp)

    # this speaker doesn't exist, but we should still send the email
    postdata = {"email": "*****@*****.**", "csrf_token": csrf_token}
    resp = client.post("/talks/{}/speakers".format(talk.talk_id),
                       data=postdata,
                       follow_redirects=True)

    assert_html_response_contains(resp, "*****@*****.**")

    send_mail.assert_called_once_with(to=["*****@*****.**"],
                                      template="email/co-presenter-invite",
                                      talk=ANY)

    _, kwargs = send_mail.call_args
    assert kwargs["talk"].talk_id == talk.talk_id

    # this also implies a user with that email was created
    assert_talk_has_speakers(talk, ["*****@*****.**"])
Exemple #3
0
def test_inviting_a_speaker_adds_the_speaker(client: Client, user: User,
                                             send_mail: Mock) -> None:
    talk = Talk(title="My Talk", length=25)
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(talk)

    alice = User(email="*****@*****.**", fullname="Alice Example")
    db.session.add(alice)
    db.session.commit()

    # reload from DB to avoid "not attached to session" error
    talk = Talk.query.filter_by(title="My Talk").one()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks/{}/speakers".format(talk.talk_id))
    assert_html_response(resp)
    csrf_token = extract_csrf_from(resp)

    postdata = {"email": "*****@*****.**", "csrf_token": csrf_token}
    resp = client.post("/talks/{}/speakers".format(talk.talk_id),
                       data=postdata,
                       follow_redirects=True)

    assert_html_response_contains(resp, "Alice Example")

    send_mail.assert_called_once_with(to=["*****@*****.**"],
                                      template="email/co-presenter-invite",
                                      talk=ANY)

    _, kwargs = send_mail.call_args
    assert kwargs["talk"].talk_id == talk.talk_id

    assert_talk_has_speakers(talk, ["*****@*****.**"])
Exemple #4
0
def create_talk() -> Response:
    talk = Talk(accepted_recording_release=True)
    talk.add_speaker(g.user, InvitationStatus.CONFIRMED)
    form = TalkForm(conference=g.conference, obj=talk)
    if form.validate_on_submit():
        form.populate_obj(talk)
        db.session.add(talk)
        db.session.commit()
        return redirect(url_for("views.preview_talk", talk_id=talk.talk_id))

    return render_template("edit_talk.html", talk=talk, form=form)
Exemple #5
0
def test_talk_anonymization(client: Client, user: User,
                            send_mail: Mock) -> None:
    user.site_admin = True
    db.session.add(user)

    talk = Talk(
        title="Alice's Identifying Talk",
        description="This talk is by Alice",
        outline="Alice!",
        take_aways="Alice's point.",
        length=25,
    )
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(talk)
    db.session.commit()

    db.session.refresh(talk)

    client.get("/test-login/{}".format(user.user_id), follow_redirects=True)
    resp = client.get(f"/manage/anonymize/{talk.talk_id}")
    assert_html_response_contains(resp, "Alice's Identifying Talk")

    postdata = {
        "title": "(Speaker name redacted)'s Identifying Talk",
        "description": "This talk is by (Speaker name redacted)",
        "outline": "(Speaker name redacted)!",
        "take_aways": "(The speaker's) point.",
        "csrf_token": extract_csrf_from(resp),
    }
    client.post(f"/manage/anonymize/{talk.talk_id}", data=postdata)

    talk = Talk.query.get(talk.talk_id)
    assert talk.is_anonymized is True
    assert talk.has_anonymization_changes is True
    assert talk.anonymized_title == "(Speaker name redacted)'s Identifying Talk"
    assert talk.anonymized_description == "This talk is by (Speaker name redacted)"
    assert talk.anonymized_outline == "(Speaker name redacted)!"
    assert talk.anonymized_take_aways == "(The speaker's) point."
    assert talk.title == "Alice's Identifying Talk"
    assert talk.description == "This talk is by Alice"
    assert talk.outline == "Alice!"
    assert talk.take_aways == "Alice's point."

    send_mail.assert_called_once_with(
        to=[user.email],
        template="email/talk-anonymized",
        talk_id=talk.talk_id,
        title=talk.title,  # the original title
    )
Exemple #6
0
def test_prompt_for_demographic_survey(client: Client, user: User) -> None:
    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks")

    assert_html_response_doesnt_contain(resp, "demographic survey")

    talk = Talk(title="My Talk", length=25)
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(talk)
    db.session.commit()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks")

    assert_html_response_contains(resp, "demographic_survey")
Exemple #7
0
def test_vote_review_shows_your_vote_and_conduct_form(
    *,
    vote_value: Optional[int],
    skipped: bool,
    vote_note: str,
    authenticated_client: Client,
    user: User,
) -> None:
    """Show the user's vote results, if present, and the conduct report form."""
    talk = Talk(
        title="",
        length=1,
        is_anonymized=True,
        anonymized_title="",
        anonymized_description="",
        anonymized_outline="",
        anonymized_take_aways="",
    )
    conference = Conference.query.first()
    category = Category(name="", conference=conference)
    category.talks.append(talk)
    db.session.add_all((category, talk))
    if vote_value is not None:
        vote = Vote(talk=talk, user=user, value=vote_value, skipped=False)
        db.session.add(vote)
    elif skipped:
        vote = Vote(talk=talk, user=user, value=None, skipped=True)
        db.session.add(vote)
    db.session.commit()

    resp = authenticated_client.get(f"/review/{talk.talk_id}")
    assert_html_response_contains(resp, vote_note)
    assert_html_response_contains(
        resp, '<form method="POST" action="/conduct-report">')
Exemple #8
0
def test_speakers_button_shows_up_on_existing_talks(client: Client,
                                                    user: User) -> None:
    talk = Talk(title="My Talk", length=25)
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(talk)
    db.session.commit()

    # reload from DB to avoid "not attached to session" error
    talk = Talk.query.filter_by(title="My Talk").one()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks/{}".format(talk.talk_id))

    assert_html_response_contains(
        resp,
        re.compile('<a href="/talks/{}/speakers".*class=".*btn.*">'
                   "Manage Speakers</a>".format(talk.talk_id)),
    )
Exemple #9
0
def test_vote_gating(*, authenticated_client: Client, conference: Conference,
                     user: User) -> None:
    """Test that votes are gated correctly for a category."""
    talk1 = Talk(title="", length=1, is_anonymized=True)
    talk2 = Talk(title="", length=1, is_anonymized=True)
    category = Category(conference=conference, name="")
    vote = Vote(talk=talk1, user=user)
    category.talks.append(talk1)
    category.talks.append(talk2)
    db.session.add_all((category, talk1, talk2, vote))
    db.session.commit()
    public_id = str(vote.public_id)

    resp = authenticated_client.get(f"/vote/category/{category.category_id}")

    assert resp.status_code == 302
    assert resp.headers["Location"].endswith(f"/vote/cast/{public_id}")
    assert Vote.query.count() == 1
    assert Vote.query.filter_by(talk_id=2).count() == 0
Exemple #10
0
def test_talks_list_page_doesnt_show_resubmit_after_proposal_window(
    client: Client, user: User
) -> None:
    conf = Conference.query.get(1)
    conf.proposals_begin = datetime.utcnow() - timedelta(days=3)
    conf.proposals_end = datetime.utcnow() - timedelta(days=1)

    withdrawn_talk = Talk(title="Withdrawn Talk", length=40, state=TalkStatus.WITHDRAWN)
    withdrawn_talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(withdrawn_talk)
    db.session.commit()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks")
    assert_html_response_doesnt_contain(resp, "Re-Submit")

    # also make sure to prevent it server-side
    resp = client.get(f"/talks/1/resubmit")
    assert_html_response(resp, status=400)
Exemple #11
0
def test_talk_editing_not_allowed_while_voting(user: User,
                                               client: Client) -> None:
    conf = Conference.query.get(1)
    conf.voting_begin = datetime.utcnow() - timedelta(days=1)
    conf.voting_end = datetime.utcnow() + timedelta(days=1)
    db.session.add(conf)

    talk = Talk(title="My Talk", length=25)
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(talk)
    db.session.commit()

    db.session.refresh(talk)
    client.get("/test-login/{}".format(user.user_id), follow_redirects=True)
    resp = client.get(f"/talks/{talk.talk_id}")
    assert_html_response(resp, status=200)

    postdata = {"csrf_token": extract_csrf_from(resp)}
    resp = client.post(f"/talks/{talk.talk_id}", data=postdata)
    assert_html_response(resp, status=400)
Exemple #12
0
def test_choose_vote(*, authenticated_client: Client, conference: Conference,
                     user: User) -> None:
    """Test that votes are created correctly for a category."""
    talk1 = Talk(title="", length=1, is_anonymized=True)
    category1 = Category(conference=conference, name="1")
    category1.talks.append(talk1)

    talk2 = Talk(title="", length=1, is_anonymized=True)
    category2 = Category(conference=conference, name="2")
    category2.talks.append(talk2)

    db.session.add_all((talk1, talk2, category1, category2))
    db.session.commit()

    resp = authenticated_client.get(f"/vote/category/{category2.category_id}")

    vote = Vote.query.filter_by(talk_id=2).first()
    assert resp.status_code == 302
    assert resp.headers["Location"].endswith(f"/vote/cast/{vote.public_id}")
    assert Vote.query.filter_by(talk_id=1).count() == 0
    assert Vote.query.filter_by(talk_id=2).count() == 1
Exemple #13
0
def test_talk_anonymization_doesnt_set_is_anonymized_if_no_changes(
        client: Client, user: User, send_mail: Mock) -> None:
    user.site_admin = True
    db.session.add(user)

    talk = Talk(
        title="Alice's Identifying Talk",
        description="This talk is by Alice",
        outline="Alice!",
        take_aways="Alice's point.",
        length=25,
    )
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(talk)
    db.session.commit()

    db.session.refresh(talk)

    client.get("/test-login/{}".format(user.user_id), follow_redirects=True)
    resp = client.get(f"/manage/anonymize/{talk.talk_id}")
    assert_html_response_contains(resp, "Alice&#x27;s Identifying Talk")

    postdata = {
        "title": talk.title,
        "description": talk.description,
        "outline": talk.outline,
        "take_aways": talk.take_aways,
        "csrf_token": extract_csrf_from(resp),
    }
    client.post(f"/manage/anonymize/{talk.talk_id}", data=postdata)

    talk = Talk.query.get(talk.talk_id)
    assert talk.is_anonymized is True
    assert talk.has_anonymization_changes is False
    assert talk.anonymized_title == talk.title
    assert talk.anonymized_description == talk.anonymized_description
    assert talk.anonymized_outline == talk.outline
    assert talk.anonymized_take_aways == talk.take_aways

    assert not send_mail.called
Exemple #14
0
def test_saving_a_talk_clears_categories(
    client: Client, conference: Conference, user: User
) -> None:
    category = Category(conference=conference, name="The Category")
    talk = Talk(title="Old Title", length=25)
    talk.categories.append(category)
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(talk)
    db.session.add(category)
    db.session.commit()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks/1")

    csrf_token = extract_csrf_from(resp)
    postdata = {"title": "New Title", "csrf_token": csrf_token}
    resp = client.post("/talks/1", data=postdata, follow_redirects=True)
    assert_html_response(resp, status=200)

    talk = Talk.query.first()
    assert talk.title == "New Title"
    assert talk.categories == []
Exemple #15
0
def test_manage_speakers_page_shows_other_speakers(client: Client,
                                                   user: User) -> None:
    alice = User(email="*****@*****.**", fullname="Alice Example")
    bob = User(email="*****@*****.**", fullname="Bob Example")
    charlie = User(email="*****@*****.**", fullname="Charlie Example")
    db.session.add(alice)
    db.session.add(bob)
    db.session.add(charlie)

    talk = Talk(title="My Talk", length=25)
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    talk.add_speaker(alice, InvitationStatus.PENDING)
    talk.add_speaker(bob, InvitationStatus.REJECTED)
    talk.add_speaker(charlie, InvitationStatus.DELETED)
    db.session.add(talk)
    db.session.commit()

    # reload from DB to avoid "not attached to session" error
    talk = Talk.query.filter_by(title="My Talk").one()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks/{}/speakers".format(talk.talk_id))

    body = assert_html_response(resp)
    soup = bs4.BeautifulSoup(body, "html.parser")

    speakers = soup.find_all("div", class_="speaker")
    assert len(speakers) == 4

    row_texts = [
        re.sub(r"\s+", " ", row.get_text()).strip() for row in speakers
    ]
    assert sorted(row_texts) == sorted([
        "{} ({}) Confirmed".format(user.fullname, user.email),
        "Alice Example ([email protected]) Pending Uninvite",
        "Bob Example ([email protected]) Rejected",
        "Charlie Example ([email protected]) Deleted Reinvite",
    ])
Exemple #16
0
def test_manage_speakers_page_shows_primary_speaker(client: Client,
                                                    user: User) -> None:
    talk = Talk(title="My Talk", length=25)
    talk.add_speaker(user, InvitationStatus.CONFIRMED)
    db.session.add(talk)
    db.session.commit()

    # reload from DB to avoid "not attached to session" error
    talk = Talk.query.filter_by(title="My Talk").one()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks/{}/speakers".format(talk.talk_id))

    body = assert_html_response(resp)
    soup = bs4.BeautifulSoup(body, "html.parser")

    speakers = soup.find_all("div", class_="speaker")
    assert len(speakers) == 1

    row_texts = [
        re.sub(r"\s+", " ", row.get_text()).strip() for row in speakers
    ]
    assert row_texts == ["{} ({}) Confirmed".format(user.fullname, user.email)]
Exemple #17
0
def test_talks_list_page_shows_proposed_and_withdrawn_talks(
    client: Client, user: User
) -> None:
    in_talk = Talk(title="In Talk", length=25)
    in_talk.add_speaker(user, InvitationStatus.CONFIRMED)

    out_talk = Talk(title="Out Talk", length=40, state=TalkStatus.WITHDRAWN)
    out_talk.add_speaker(user, InvitationStatus.CONFIRMED)

    db.session.add(in_talk)
    db.session.add(out_talk)
    db.session.commit()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks")
    body = assert_html_response(resp)
    soup = bs4.BeautifulSoup(body, "html.parser")

    talks = soup.find_all("div", class_="talk")
    assert len(talks) == 2

    talk_row_texts = [
        re.sub(r"\s+", " ", talk.parent.get_text()).strip() for talk in talks
    ]
    talk_row_texts.sort()

    assert re.match("In Talk.*Withdraw", talk_row_texts[0])
    assert re.match("Out Talk.*Re-Submit", talk_row_texts[1])

    # lazy: also test withdraw/resubmit here in the same test
    client.get(f"/talks/1/withdraw")
    client.get(f"/talks/2/resubmit")

    resp = client.get("/talks")
    body = assert_html_response(resp)
    soup = bs4.BeautifulSoup(body, "html.parser")

    talks = soup.find_all("div", class_="talk")
    assert len(talks) == 2

    talk_row_texts = [
        re.sub(r"\s+", " ", talk.parent.get_text()).strip() for talk in talks
    ]
    talk_row_texts.sort()

    assert re.match("In Talk.*Re-Submit", talk_row_texts[0])
    assert re.match("Out Talk.*Withdraw", talk_row_texts[1])
Exemple #18
0
def test_vote_save(*, authenticated_client: Client, user: User) -> None:
    """Test that a vote can be saved."""
    talk = Talk(title="", length=1)
    vote = Vote(talk=talk, user=user)
    db.session.add_all((talk, vote))
    db.session.commit()
    public_id = str(vote.public_id)

    resp = authenticated_client.post(f"/vote/cast/{vote.public_id}",
                                     data={
                                         "action": "vote",
                                         "value": 1
                                     })

    assert resp.status_code == 302
    assert str(Vote.query.filter_by(value=1).first().public_id) == public_id
Exemple #19
0
def test_report_conduct_issue(*, authenticated_client: Client, send_mail: Mock,
                              user: User) -> None:
    """Test that a user can successfully report a CoC issue."""
    talk = Talk(title="", length=1)
    db.session.add(talk)
    db.session.commit()
    resp = authenticated_client.post(
        "/conduct-report",
        data={
            "talk_id": talk.talk_id,
            "text": "Please review",
            "anonymous": 0
        },
    )

    send_mail.assert_called_once()
    assert resp.status_code == 302
    assert ConductReport.query.filter_by(user=user).count() == 1
Exemple #20
0
def test_accept_button_accepts_the_talk(client: Client, user: User) -> None:
    alice = User(email="*****@*****.**", fullname="Alice Example")
    db.session.add(alice)

    talk = Talk(title="My Talk", length=25)
    talk.add_speaker(alice, InvitationStatus.CONFIRMED)  # original speaker
    talk.add_speaker(user, InvitationStatus.PENDING)
    db.session.add(talk)
    db.session.commit()

    client.get("/test-login/{}".format(user.user_id))
    client.get("/talks/1/speakers/accept")
    resp = client.get("/talks")

    assert_html_response_contains(resp, "My Talk",
                                  "(25 Minutes, Alice Example and You)")
    assert_html_response_doesnt_contain(resp, "Speaker Invitations:", "Reject",
                                        "Accept")
Exemple #21
0
def test_talks_list_shows_invitations(client: Client, user: User) -> None:
    alice = User(email="*****@*****.**", fullname="Alice Example")
    db.session.add(alice)

    talk = Talk(title="My Talk", length=25)
    talk.add_speaker(alice, InvitationStatus.CONFIRMED)  # original speaker
    talk.add_speaker(user, InvitationStatus.PENDING)
    db.session.add(talk)
    db.session.commit()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks")

    assert_html_response_contains(
        resp,
        "Speaker Invitations:",
        "My Talk",
        "(25 Minutes, Alice Example and You)",
        '<a href="/talks/1/speakers/reject" class="btn btn-outline-danger btn-sm">Reject</a>',  # NOQA: B950
        '<a href="/talks/1/speakers/accept" class="btn btn-outline-primary btn-sm">Accept</a>',  # NOQA: B950
    )
Exemple #22
0
def test_talks_list_page_lists_talks(client: Client, user: User) -> None:
    alice = User(email="*****@*****.**", fullname="Alice Example")
    bob = User(email="*****@*****.**", fullname="Bob Example")
    db.session.add(alice)
    db.session.add(bob)
    db.session.commit()

    one_talk = Talk(title="My Talk", length=25)
    one_talk.add_speaker(user, InvitationStatus.CONFIRMED)

    two_talk = Talk(title="Our Talk", length=40)
    two_talk.add_speaker(user, InvitationStatus.CONFIRMED)
    two_talk.add_speaker(alice, InvitationStatus.CONFIRMED)

    all_talk = Talk(title="All Our Talk", length=25)
    all_talk.add_speaker(user, InvitationStatus.CONFIRMED)
    all_talk.add_speaker(alice, InvitationStatus.CONFIRMED)
    all_talk.add_speaker(bob, InvitationStatus.CONFIRMED)

    db.session.add(one_talk)
    db.session.add(two_talk)
    db.session.add(all_talk)
    db.session.commit()

    client.get("/test-login/{}".format(user.user_id))
    resp = client.get("/talks")
    body = assert_html_response(resp)
    soup = bs4.BeautifulSoup(body, "html.parser")

    talks = soup.find_all("div", class_="talk")
    assert len(talks) == 3

    talk_row_texts = [re.sub(r"\s+", " ", talk.get_text()).strip() for talk in talks]
    assert sorted(talk_row_texts) == sorted(
        [
            "My Talk (25 Minutes)",
            "Our Talk (40 Minutes, Alice Example and You)",
            "All Our Talk (25 Minutes, Alice Example, Bob Example, and You)",
        ]
    )