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, ["*****@*****.**"])
def email_magic_link_login(magic_link_token: str) -> Response: try: used_magic_link = UsedMagicLink(token=magic_link_token) db.session.add(used_magic_link) db.session.commit() except IntegrityError: # race condition, UsedMagicLink row exists db.session.rollback() return current_app.response_class( render_template("email_magic_link_used.html"), status=401) verified_email = parse_magic_link_token(magic_link_token) if verified_email is None: abort(404) user = User.query.filter_by(email=verified_email).first() if user: login_user(user) return redirect(url_for("views.talks_list")) user = User(email=verified_email, fullname=verified_email) db.session.add(user) db.session.commit() login_user(user) flash("Welcome! Please tell us your name.") return redirect(url_for("views.user_profile"))
def test_site_admins_can_access_admin(client: Client, user: User) -> None: client.get("/test-login/{}".format(user.user_id), follow_redirects=True) user.site_admin = True db.session.add(user) db.session.commit() resp = client.get("/manage/") assert_html_response(resp, status=200)
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)", ] )
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", ])
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 )
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")
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 )
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'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
def edit_speakers(talk_id: int) -> Response: talk = load_talk(talk_id) actions = { InvitationStatus.PENDING: [("Uninvite", "danger", "views.uninvite_speaker")], InvitationStatus.CONFIRMED: [], InvitationStatus.REJECTED: [], InvitationStatus.DELETED: [("Reinvite", "primary", "views.reinvite_speaker")], } speaker_emails = [s.user.email for s in talk.speakers] form = SpeakerEmailForm(speaker_emails) if form.validate_on_submit(): email = form.email.data try: user = User(fullname=email, email=email) db.session.add(user) db.session.commit() except IntegrityError: db.session.rollback() user = User.query.filter_by(email=email).one() try: talk.add_speaker(user, InvitationStatus.PENDING) db.session.commit() except IntegrityError: db.session.rollback() mail.send_mail(to=[email], template="email/co-presenter-invite", talk=talk) return redirect(url_for("views.edit_speakers", talk_id=talk.talk_id)) return render_template("edit_speakers.html", talk=talk, actions=actions, form=form)
def test_profile_lets_you_delete_speaker_bio(client: Client, user: User) -> None: user.speaker_bio = "This is the speaker bio" db.session.add(user) db.session.commit() client.get("/test-login/{}".format(user.user_id), follow_redirects=True) resp = client.get("/profile") assert_html_response_contains(resp, "This is the speaker bio") csrf_token = extract_csrf_from(resp) postdata = { "email": "*****@*****.**", "fullname": "Jane Doe", "speaker_bio": "", "csrf_token": csrf_token, } resp = client.post("/profile", data=postdata, follow_redirects=True) assert_html_response_contains(resp, "Talks") db.session.add(user) db.session.refresh(user) assert user.fullname == "Jane Doe" assert user.speaker_bio == "" assert user.email == "*****@*****.**" # the old address
def user(app: Application) -> User: user = User(fullname="Test User", email="*****@*****.**") db.session.add(user) db.session.commit() return user