chore(travis): update to mastodon v2.6.1 (#630)

* chore(travis): update to mastodon v2.6.1

* check if mastodon v2.6.1 has a race condition

* apparently in 2.6.1 direct messages no longer appear in home timeline

* Revert "check if mastodon v2.6.1 has a race condition"

This reverts commit dde8ef8be58eda0563170e6b73165fdcbea54f6b.

* try to fix tests

* fix more tests
This commit is contained in:
Nolan Lawson 2018-11-12 12:59:47 -08:00 committed by GitHub
parent 1fa37df59a
commit 0964442815
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 301 additions and 104 deletions

View File

@ -14,7 +14,7 @@ const writeFile = pify(fs.writeFile.bind(fs))
const dir = __dirname
const GIT_URL = 'https://github.com/tootsuite/mastodon.git'
const GIT_TAG = 'v2.5.0'
const GIT_TAG = 'v2.6.1'
const DB_NAME = 'pinafore_development'
const DB_USER = 'pinafore'

View File

@ -8,8 +8,8 @@ fi
# install ruby
source "$HOME/.rvm/scripts/rvm"
rvm install 2.5.1
rvm use 2.5.1
rvm install 2.5.3
rvm use 2.5.3
# fix for redis IPv6 issue
# https://travis-ci.community/t/trusty-environment-redis-server-not-starting-with-redis-tools-installed/650/2

View File

@ -82,6 +82,45 @@ SET default_tablespace = '';
SET default_with_oids = false;
--
-- Name: account_conversations; Type: TABLE; Schema: public; Owner: pinafore
--
CREATE TABLE public.account_conversations (
id bigint NOT NULL,
account_id bigint,
conversation_id bigint,
participant_account_ids bigint[] DEFAULT '{}'::bigint[] NOT NULL,
status_ids bigint[] DEFAULT '{}'::bigint[] NOT NULL,
last_status_id bigint,
lock_version integer DEFAULT 0 NOT NULL,
unread boolean DEFAULT false NOT NULL
);
ALTER TABLE public.account_conversations OWNER TO pinafore;
--
-- Name: account_conversations_id_seq; Type: SEQUENCE; Schema: public; Owner: pinafore
--
CREATE SEQUENCE public.account_conversations_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.account_conversations_id_seq OWNER TO pinafore;
--
-- Name: account_conversations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: pinafore
--
ALTER SEQUENCE public.account_conversations_id_seq OWNED BY public.account_conversations.id;
--
-- Name: account_domain_blocks; Type: TABLE; Schema: public; Owner: pinafore
--
@ -558,7 +597,8 @@ CREATE TABLE public.domain_blocks (
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
severity integer DEFAULT 0,
reject_media boolean DEFAULT false NOT NULL
reject_media boolean DEFAULT false NOT NULL,
reject_reports boolean DEFAULT false NOT NULL
);
@ -976,7 +1016,8 @@ CREATE TABLE public.mentions (
status_id bigint,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
account_id bigint
account_id bigint,
silent boolean DEFAULT false NOT NULL
);
@ -1202,6 +1243,43 @@ ALTER TABLE public.oauth_applications_id_seq OWNER TO pinafore;
ALTER SEQUENCE public.oauth_applications_id_seq OWNED BY public.oauth_applications.id;
--
-- Name: pghero_space_stats; Type: TABLE; Schema: public; Owner: pinafore
--
CREATE TABLE public.pghero_space_stats (
id bigint NOT NULL,
database text,
schema text,
relation text,
size bigint,
captured_at timestamp without time zone
);
ALTER TABLE public.pghero_space_stats OWNER TO pinafore;
--
-- Name: pghero_space_stats_id_seq; Type: SEQUENCE; Schema: public; Owner: pinafore
--
CREATE SEQUENCE public.pghero_space_stats_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.pghero_space_stats_id_seq OWNER TO pinafore;
--
-- Name: pghero_space_stats_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: pinafore
--
ALTER SEQUENCE public.pghero_space_stats_id_seq OWNED BY public.pghero_space_stats.id;
--
-- Name: preview_cards; Type: TABLE; Schema: public; Owner: pinafore
--
@ -1890,6 +1968,13 @@ ALTER TABLE public.web_settings_id_seq OWNER TO pinafore;
ALTER SEQUENCE public.web_settings_id_seq OWNED BY public.web_settings.id;
--
-- Name: account_conversations id; Type: DEFAULT; Schema: public; Owner: pinafore
--
ALTER TABLE ONLY public.account_conversations ALTER COLUMN id SET DEFAULT nextval('public.account_conversations_id_seq'::regclass);
--
-- Name: account_domain_blocks id; Type: DEFAULT; Schema: public; Owner: pinafore
--
@ -2086,6 +2171,13 @@ ALTER TABLE ONLY public.oauth_access_tokens ALTER COLUMN id SET DEFAULT nextval(
ALTER TABLE ONLY public.oauth_applications ALTER COLUMN id SET DEFAULT nextval('public.oauth_applications_id_seq'::regclass);
--
-- Name: pghero_space_stats id; Type: DEFAULT; Schema: public; Owner: pinafore
--
ALTER TABLE ONLY public.pghero_space_stats ALTER COLUMN id SET DEFAULT nextval('public.pghero_space_stats_id_seq'::regclass);
--
-- Name: preview_cards id; Type: DEFAULT; Schema: public; Owner: pinafore
--
@ -2191,6 +2283,14 @@ ALTER TABLE ONLY public.web_push_subscriptions ALTER COLUMN id SET DEFAULT nextv
ALTER TABLE ONLY public.web_settings ALTER COLUMN id SET DEFAULT nextval('public.web_settings_id_seq'::regclass);
--
-- Data for Name: account_conversations; Type: TABLE DATA; Schema: public; Owner: pinafore
--
COPY public.account_conversations (id, account_id, conversation_id, participant_account_ids, status_ids, last_status_id, lock_version, unread) FROM stdin;
\.
--
-- Data for Name: account_domain_blocks; Type: TABLE DATA; Schema: public; Owner: pinafore
--
@ -2304,7 +2404,7 @@ COPY public.custom_filters (id, account_id, expires_at, phrase, context, irrever
-- Data for Name: domain_blocks; Type: TABLE DATA; Schema: public; Owner: pinafore
--
COPY public.domain_blocks (id, domain, created_at, updated_at, severity, reject_media) FROM stdin;
COPY public.domain_blocks (id, domain, created_at, updated_at, severity, reject_media, reject_reports) FROM stdin;
\.
@ -2397,7 +2497,7 @@ COPY public.media_attachments (id, status_id, file_file_name, file_content_type,
-- Data for Name: mentions; Type: TABLE DATA; Schema: public; Owner: pinafore
--
COPY public.mentions (id, status_id, created_at, updated_at, account_id) FROM stdin;
COPY public.mentions (id, status_id, created_at, updated_at, account_id, silent) FROM stdin;
\.
@ -2455,6 +2555,14 @@ COPY public.oauth_applications (id, name, uid, secret, redirect_uri, scopes, cre
\.
--
-- Data for Name: pghero_space_stats; Type: TABLE DATA; Schema: public; Owner: pinafore
--
COPY public.pghero_space_stats (id, database, schema, relation, size, captured_at) FROM stdin;
\.
--
-- Data for Name: preview_cards; Type: TABLE DATA; Schema: public; Owner: pinafore
--
@ -2678,6 +2786,13 @@ COPY public.schema_migrations (version) FROM stdin;
20180813113448
20180814171349
20180820232245
20180929222014
20181007025445
20181010141500
20181017170937
20181018205649
20181024224956
20181026034033
\.
@ -2805,6 +2920,13 @@ COPY public.web_settings (id, data, created_at, updated_at, user_id) FROM stdin;
\.
--
-- Name: account_conversations_id_seq; Type: SEQUENCE SET; Schema: public; Owner: pinafore
--
SELECT pg_catalog.setval('public.account_conversations_id_seq', 1, false);
--
-- Name: account_domain_blocks_id_seq; Type: SEQUENCE SET; Schema: public; Owner: pinafore
--
@ -3001,6 +3123,13 @@ SELECT pg_catalog.setval('public.oauth_access_tokens_id_seq', 9, true);
SELECT pg_catalog.setval('public.oauth_applications_id_seq', 1, true);
--
-- Name: pghero_space_stats_id_seq; Type: SEQUENCE SET; Schema: public; Owner: pinafore
--
SELECT pg_catalog.setval('public.pghero_space_stats_id_seq', 1, false);
--
-- Name: preview_cards_id_seq; Type: SEQUENCE SET; Schema: public; Owner: pinafore
--
@ -3113,6 +3242,14 @@ SELECT pg_catalog.setval('public.web_push_subscriptions_id_seq', 1, false);
SELECT pg_catalog.setval('public.web_settings_id_seq', 4, true);
--
-- Name: account_conversations account_conversations_pkey; Type: CONSTRAINT; Schema: public; Owner: pinafore
--
ALTER TABLE ONLY public.account_conversations
ADD CONSTRAINT account_conversations_pkey PRIMARY KEY (id);
--
-- Name: account_domain_blocks account_domain_blocks_pkey; Type: CONSTRAINT; Schema: public; Owner: pinafore
--
@ -3345,6 +3482,14 @@ ALTER TABLE ONLY public.oauth_applications
ADD CONSTRAINT oauth_applications_pkey PRIMARY KEY (id);
--
-- Name: pghero_space_stats pghero_space_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: pinafore
--
ALTER TABLE ONLY public.pghero_space_stats
ADD CONSTRAINT pghero_space_stats_pkey PRIMARY KEY (id);
--
-- Name: preview_cards preview_cards_pkey; Type: CONSTRAINT; Schema: public; Owner: pinafore
--
@ -3495,6 +3640,20 @@ CREATE UNIQUE INDEX account_activity ON public.notifications USING btree (accoun
CREATE INDEX hashtag_search_index ON public.tags USING btree (lower((name)::text) text_pattern_ops);
--
-- Name: index_account_conversations_on_account_id; Type: INDEX; Schema: public; Owner: pinafore
--
CREATE INDEX index_account_conversations_on_account_id ON public.account_conversations USING btree (account_id);
--
-- Name: index_account_conversations_on_conversation_id; Type: INDEX; Schema: public; Owner: pinafore
--
CREATE INDEX index_account_conversations_on_conversation_id ON public.account_conversations USING btree (conversation_id);
--
-- Name: index_account_domain_blocks_on_account_id_and_domain; Type: INDEX; Schema: public; Owner: pinafore
--
@ -3845,6 +4004,13 @@ CREATE INDEX index_oauth_applications_on_owner_id_and_owner_type ON public.oauth
CREATE UNIQUE INDEX index_oauth_applications_on_uid ON public.oauth_applications USING btree (uid);
--
-- Name: index_pghero_space_stats_on_database_and_captured_at; Type: INDEX; Schema: public; Owner: pinafore
--
CREATE INDEX index_pghero_space_stats_on_database_and_captured_at ON public.pghero_space_stats USING btree (database, captured_at);
--
-- Name: index_preview_cards_on_url; Type: INDEX; Schema: public; Owner: pinafore
--
@ -4013,6 +4179,13 @@ CREATE UNIQUE INDEX index_subscriptions_on_account_id_and_callback_url ON public
CREATE UNIQUE INDEX index_tags_on_name ON public.tags USING btree (name);
--
-- Name: index_unique_conversations; Type: INDEX; Schema: public; Owner: pinafore
--
CREATE UNIQUE INDEX index_unique_conversations ON public.account_conversations USING btree (account_id, conversation_id, participant_account_ids);
--
-- Name: index_users_on_account_id; Type: INDEX; Schema: public; Owner: pinafore
--
@ -4357,6 +4530,14 @@ ALTER TABLE ONLY public.backups
ADD CONSTRAINT fk_rails_096669d221 FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL;
--
-- Name: account_conversations fk_rails_1491654f9f; Type: FK CONSTRAINT; Schema: public; Owner: pinafore
--
ALTER TABLE ONLY public.account_conversations
ADD CONSTRAINT fk_rails_1491654f9f FOREIGN KEY (conversation_id) REFERENCES public.conversations(id) ON DELETE CASCADE;
--
-- Name: accounts fk_rails_2320833084; Type: FK CONSTRAINT; Schema: public; Owner: pinafore
--
@ -4453,6 +4634,14 @@ ALTER TABLE ONLY public.status_pins
ADD CONSTRAINT fk_rails_65c05552f1 FOREIGN KEY (status_id) REFERENCES public.statuses(id) ON DELETE CASCADE;
--
-- Name: account_conversations fk_rails_6f5278b6e9; Type: FK CONSTRAINT; Schema: public; Owner: pinafore
--
ALTER TABLE ONLY public.account_conversations
ADD CONSTRAINT fk_rails_6f5278b6e9 FOREIGN KEY (account_id) REFERENCES public.accounts(id) ON DELETE CASCADE;
--
-- Name: web_push_subscriptions fk_rails_751a9f390b; Type: FK CONSTRAINT; Schema: public; Owner: pinafore
--

Binary file not shown.

View File

@ -44,3 +44,12 @@ export function concat () {
}
return res
}
export function indexWhere (arr, cb) {
for (let i = 0; i < arr.length; i++) {
if (cb(arr[i], i)) {
return i
}
}
return -1
}

View File

@ -4,10 +4,8 @@ export const homeTimeline = [
{ content: 'pinned toot 1' },
{ content: 'notification of unlisted message' },
{ content: 'notification of followers-only message' },
{ content: 'notification of direct message' },
{ content: 'this is unlisted' },
{ content: 'this is followers-only' },
{ content: 'direct' },
{ spoiler: 'kitten CW' },
{ content: 'secret video' },
{ content: "here's a video" },

View File

@ -22,7 +22,7 @@ test('Shows the home timeline', async t => {
await validateTimeline(t, homeTimeline)
await t.expect(getFirstVisibleStatus().getAttribute('aria-setsize')).eql('49')
await t.expect(getFirstVisibleStatus().getAttribute('aria-setsize')).eql('47')
})
test('Shows notifications', async t => {

View File

@ -5,7 +5,7 @@ import { Selector as $ } from 'testcafe'
fixture`005-status-types.js`
.page`http://localhost:4002`
test('shows direct vs followers-only vs regular', async t => {
test('shows followers-only vs regular in home timeline', async t => {
await loginAsFoobar(t)
await t
.expect(getNthStatus(1).getAttribute('aria-label')).eql('Status by admin')
@ -18,11 +18,6 @@ test('shows direct vs followers-only vs regular', async t => {
.expect($(`${getNthStatusSelector(2)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Cannot be boosted because this is followers-only')
.expect($(`${getNthStatusSelector(2)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).ok()
.expect(getNthStatus(3).getAttribute('aria-label')).eql('Direct message by admin')
.expect($(`${getNthStatusSelector(3)} .status-content`).innerText).contains('notification of direct message')
.expect($(`${getNthStatusSelector(3)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Cannot be boosted because this is a direct message')
.expect($(`${getNthStatusSelector(3)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).ok()
})
test('shows direct vs followers-only vs regular in notifications', async t => {

View File

@ -1,34 +1,44 @@
import { closeDialogButton, getNthStatus, getNthStatusSelector, modalDialogContents, scrollToStatus } from '../utils'
import { loginAsFoobar } from '../roles'
import { Selector as $ } from 'testcafe'
import { homeTimeline } from '../fixtures'
import { indexWhere } from '../../routes/_utils/arrays'
fixture`008-status-media.js`
.page`http://localhost:4002`
test('shows sensitive images and videos', async t => {
await loginAsFoobar(t)
await scrollToStatus(t, 7)
await t.expect($(`${getNthStatusSelector(7)} .status-media img`).exists).notOk()
.click($(`${getNthStatusSelector(7)} .status-sensitive-media-button`))
.expect($(`${getNthStatusSelector(7)} .status-media img`).getAttribute('alt')).eql('kitten')
.expect($(`${getNthStatusSelector(7)} .status-media img`).hasAttribute('src')).ok()
.hover(getNthStatus(8))
.expect($(`${getNthStatusSelector(8)} .status-media .play-video-button`).exists).notOk()
.click($(`${getNthStatusSelector(8)} .status-sensitive-media-button`))
.expect($(`${getNthStatusSelector(8)} .status-media .play-video-button`).exists).ok()
let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW')
let videoIdx = indexWhere(homeTimeline, _ => _.content === 'secret video')
await scrollToStatus(t, kittenIdx)
await t.expect($(`${getNthStatusSelector(kittenIdx)} .status-media img`).exists).notOk()
.click($(`${getNthStatusSelector(kittenIdx)} .status-sensitive-media-button`))
.expect($(`${getNthStatusSelector(kittenIdx)} .status-media img`).getAttribute('alt')).eql('kitten')
.expect($(`${getNthStatusSelector(kittenIdx)} .status-media img`).hasAttribute('src')).ok()
.hover(getNthStatus(videoIdx))
.expect($(`${getNthStatusSelector(videoIdx)} .status-media .play-video-button`).exists).notOk()
.click($(`${getNthStatusSelector(videoIdx)} .status-sensitive-media-button`))
.expect($(`${getNthStatusSelector(videoIdx)} .status-media .play-video-button`).exists).ok()
})
test('click and close image and video modals', async t => {
await loginAsFoobar(t)
await scrollToStatus(t, 9)
let videoIdx = indexWhere(homeTimeline, _ => _.content === "here's a video")
let kittenIdx = indexWhere(homeTimeline, _ => _.content === "here's an animated kitten gif")
await scrollToStatus(t, videoIdx)
await t.expect(modalDialogContents.exists).notOk()
.click($(`${getNthStatusSelector(9)} .play-video-button`))
.click($(`${getNthStatusSelector(videoIdx)} .play-video-button`))
.expect(modalDialogContents.exists).ok()
.click(closeDialogButton)
.expect(modalDialogContents.exists).notOk()
.hover(getNthStatus(11))
.hover(getNthStatus(12))
.click($(`${getNthStatusSelector(12)} .show-image-button`))
.hover(getNthStatus(kittenIdx - 1))
.hover(getNthStatus(kittenIdx))
.click($(`${getNthStatusSelector(kittenIdx)} .show-image-button`))
.expect(modalDialogContents.exists).ok()
.click(closeDialogButton)
.expect(modalDialogContents.exists).notOk()

View File

@ -5,21 +5,26 @@ import {
} from '../utils'
import { loginAsFoobar } from '../roles'
import { Selector as $ } from 'testcafe'
import { indexWhere } from '../../routes/_utils/arrays'
import { homeTimeline } from '../fixtures'
fixture`010-focus.js`
.page`http://localhost:4002`
test('modal preserves focus', async t => {
await loginAsFoobar(t)
await scrollToStatus(t, 9)
let idx = indexWhere(homeTimeline, _ => _.content === "here's a video")
await scrollToStatus(t, idx)
// explicitly hover-focus-click
await t.hover($(`${getNthStatusSelector(9)} .play-video-button`))
await focus(`${getNthStatusSelector(9)} .play-video-button`)()
await t.click($(`${getNthStatusSelector(9)} .play-video-button`))
await t.hover($(`${getNthStatusSelector(idx)} .play-video-button`))
await focus(`${getNthStatusSelector(idx)} .play-video-button`)()
await t.click($(`${getNthStatusSelector(idx)} .play-video-button`))
.click(closeDialogButton)
.expect(modalDialogContents.exists).notOk()
.expect(getActiveElementClass()).contains('play-video-button')
.expect(getActiveElementInsideNthStatus()).eql('9')
.expect(getActiveElementInsideNthStatus()).eql(idx.toString())
})
test('timeline preserves focus', async t => {

View File

@ -6,6 +6,8 @@ import {
getNthStatus, getUrl, homeNavButton, notificationsNavButton, scrollToStatus
} from '../utils'
import { loginAsFoobar } from '../roles'
import { homeTimeline } from '../fixtures'
import { indexWhere } from '../../routes/_utils/arrays'
fixture`017-compose-reply.js`
.page`http://localhost:4002`
@ -52,48 +54,46 @@ test('replies have same privacy as replied-to status by default', async t => {
.expect(getNthPostPrivacyButton(2).getAttribute('aria-label')).eql('Adjust privacy (currently Followers-only)')
.click(getNthReplyButton(2))
.hover(getNthStatus(3))
.click(getNthReplyButton(3))
.expect(getNthPostPrivacyButton(3).getAttribute('aria-label')).eql('Adjust privacy (currently Direct)')
.click(getNthReplyButton(3))
.hover(getNthStatus(4))
.hover(getNthStatus(5))
.hover(getNthStatus(6))
.hover(getNthStatus(7))
.click(getNthReplyButton(7))
.expect(getNthPostPrivacyButton(7).getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
.click(getNthReplyButton(7))
.click(getNthReplyButton(5))
.expect(getNthPostPrivacyButton(5).getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
.click(getNthReplyButton(5))
})
test('replies have same CW as replied-to status', async t => {
await loginAsFoobar(t)
await scrollToStatus(t, 7)
await t.click(getNthReplyButton(7))
.expect(getNthReplyContentWarningInput(7).value).eql('kitten CW')
.click(getNthStatus(7))
let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW')
await scrollToStatus(t, kittenIdx)
await t.click(getNthReplyButton(kittenIdx))
.expect(getNthReplyContentWarningInput(kittenIdx).value).eql('kitten CW')
.click(getNthStatus(kittenIdx))
.click(getNthReplyButton(0))
.expect(getNthReplyContentWarningInput(0).value).eql('kitten CW')
})
test('replies save deletions of CW', async t => {
await loginAsFoobar(t)
await scrollToStatus(t, 7)
await t.click(getNthReplyButton(7))
.expect(getNthReplyContentWarningInput(7).value).eql('kitten CW')
.click(getNthReplyContentWarningButton(7))
.expect(getNthReplyContentWarningInput(7).exists).notOk()
.click(getNthStatus(7))
let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW')
await scrollToStatus(t, kittenIdx)
await t.click(getNthReplyButton(kittenIdx))
.expect(getNthReplyContentWarningInput(kittenIdx).value).eql('kitten CW')
.click(getNthReplyContentWarningButton(kittenIdx))
.expect(getNthReplyContentWarningInput(kittenIdx).exists).notOk()
.click(getNthStatus(kittenIdx))
.click(getNthReplyButton(0))
.expect(getNthReplyContentWarningInput(0).exists).notOk()
})
test('replies save changes to CW', async t => {
await loginAsFoobar(t)
await scrollToStatus(t, 7)
await t.click(getNthReplyButton(7))
.expect(getNthReplyContentWarningInput(7).value).eql('kitten CW')
.typeText(getNthReplyContentWarningInput(7), ' yolo', { paste: true })
.expect(getNthReplyContentWarningInput(7).value).eql('kitten CW yolo')
.click(getNthStatus(7))
let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW')
await scrollToStatus(t, kittenIdx)
await t.click(getNthReplyButton(kittenIdx))
.expect(getNthReplyContentWarningInput(kittenIdx).value).eql('kitten CW')
.typeText(getNthReplyContentWarningInput(kittenIdx), ' yolo', { paste: true })
.expect(getNthReplyContentWarningInput(kittenIdx).value).eql('kitten CW yolo')
.click(getNthStatus(kittenIdx))
.click(getNthReplyButton(0))
.expect(getNthReplyContentWarningInput(0).value).eql('kitten CW yolo')
})

View File

@ -4,6 +4,8 @@ import {
scrollToBottomOfTimeline, scrollToTopOfTimeline
} from '../utils'
import { loginAsFoobar } from '../roles'
import { indexWhere } from '../../routes/_utils/arrays'
import { homeTimeline } from '../fixtures'
fixture`100-favorite-unfavorite.js`
.page`http://localhost:4002`
@ -59,20 +61,21 @@ test('unfavorites a status', async t => {
test('Keeps the correct favorites count', async t => {
await loginAsFoobar(t)
let idx = indexWhere(homeTimeline, _ => _.content === 'this is unlisted')
await t
.hover(getNthStatus(4))
.click(getNthFavoriteButton(4))
.expect(getNthFavorited(4)).eql('true')
.click(getNthStatus(4))
.hover(getNthStatus(idx))
.click(getNthFavoriteButton(idx))
.expect(getNthFavorited(idx)).eql('true')
.click(getNthStatus(idx))
.expect(getUrl()).contains('/status')
.expect(getNthFavorited(0)).eql('true')
.expect(getFavoritesCount()).eql(2)
.click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/')
.hover(getNthStatus(4))
.click(getNthFavoriteButton(4))
.expect(getNthFavorited(4)).eql('false')
.click(getNthStatus(4))
.hover(getNthStatus(idx))
.click(getNthFavoriteButton(idx))
.expect(getNthFavorited(idx)).eql('false')
.click(getNthStatus(idx))
.expect(getUrl()).contains('/status')
.expect(getNthFavorited(0)).eql('false')
.expect(getFavoritesCount()).eql(1)

View File

@ -37,44 +37,44 @@ test('reblogs a status', async t => {
test('unreblogs a status', async t => {
await loginAsFoobar(t)
await t
.hover(getNthStatus(4))
.expect(getNthReblogged(4)).eql('false')
.click(getNthReblogButton(4))
.expect(getNthReblogged(4)).eql('true')
.click(getNthReblogButton(4))
.expect(getNthReblogged(4)).eql('false')
.hover(getNthStatus(3))
.expect(getNthReblogged(3)).eql('false')
.click(getNthReblogButton(3))
.expect(getNthReblogged(3)).eql('true')
.click(getNthReblogButton(3))
.expect(getNthReblogged(3)).eql('false')
// scroll down and back up to force an unrender
await scrollToBottomOfTimeline(t)
await scrollToTopOfTimeline(t)
await t
.hover(getNthStatus(4))
.expect(getNthReblogged(4)).eql('false')
.hover(getNthStatus(3))
.expect(getNthReblogged(3)).eql('false')
.click(notificationsNavButton)
.click(homeNavButton)
.expect(getNthReblogged(4)).eql('false')
.expect(getNthReblogged(3)).eql('false')
.click(notificationsNavButton)
.navigateTo('/')
.expect(getNthReblogged(4)).eql('false')
.click(getNthReblogButton(4))
.expect(getNthReblogged(4)).eql('true')
.expect(getNthReblogged(3)).eql('false')
.click(getNthReblogButton(3))
.expect(getNthReblogged(3)).eql('true')
})
test('Keeps the correct reblogs count', async t => {
await loginAsFoobar(t)
await t
.hover(getNthStatus(4))
.expect(getNthReblogged(4)).eql('true')
.click(getNthStatus(4))
.hover(getNthStatus(3))
.expect(getNthReblogged(3)).eql('true')
.click(getNthStatus(3))
.expect(getUrl()).contains('/status')
.expect(getNthReblogged(0)).eql('true')
.expect(getReblogsCount()).eql(2)
.click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/')
.hover(getNthStatus(4))
.click(getNthReblogButton(4))
.expect(getNthReblogged(4)).eql('false')
.click(getNthStatus(4))
.hover(getNthStatus(3))
.click(getNthReblogButton(3))
.expect(getNthReblogged(3)).eql('false')
.click(getNthStatus(3))
.expect(getUrl()).contains('/status')
.expect(getNthReblogged(0)).eql('false')
.expect(getReblogsCount()).eql(1)

View File

@ -1,37 +1,25 @@
import { loginAsFoobar } from '../roles'
import {
getNthStatus, getNthStatusSelector, getUrl, homeNavButton, notificationsNavButton,
validateTimeline
getNthStatus, getUrl, homeNavButton, notificationsNavButton
} from '../utils'
import { favoriteStatusAs } from '../serverActions'
import { notifications } from '../fixtures'
import { Selector as $ } from 'testcafe'
import { favoriteStatusAs, postAs } from '../serverActions'
fixture`102-notifications.js`
.page`http://localhost:4002`
test('shows unread notifications', async t => {
let { id } = await postAs('foobar', 'somebody please favorite this to validate me')
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.hover(getNthStatus(2))
.hover(getNthStatus(4))
.hover(getNthStatus(5))
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications')
let statusId = (await $(`${getNthStatusSelector(5)} .status-relative-date`).getAttribute('href'))
.split('/').slice(-1)[0]
await favoriteStatusAs('admin', statusId)
await favoriteStatusAs('admin', id)
await t
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (1)')
.click(notificationsNavButton)
.expect(getUrl()).contains('/notifications')
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (current page)')
await validateTimeline(t, [
{
favoritedBy: 'admin',
content: 'this is followers-only'
}
].concat(notifications))
.expect(getNthStatus(0).innerText).contains('somebody please favorite this to validate me')
.expect(getNthStatus(0).innerText).match(/admin\s+favorited your status/)
await t
.click(homeNavButton)
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications')