diff --git a/bin/mastodon-data.js b/bin/mastodon-data.js new file mode 100644 index 0000000..bd488fc --- /dev/null +++ b/bin/mastodon-data.js @@ -0,0 +1,220 @@ +const times = require('lodash/times') + +export const actions = times(30, i => ({ + post: { + text: (i + 1) + }, + user: 'admin' +})).concat([ + { + user: 'foobar', + post: { + text: 'hello world' + } + }, + { + user: 'foobar', + post: { + text: "here's a kitten", + media: ['kitten1.jpg'] + } + }, + { + user: 'foobar', + post: { + text: "here's a secret kitten", + media: ['kitten2.jpg'], + sensitive: true + } + }, + { + user: 'foobar', + post: { + text: "here's 2 kitten photos", + media: ['kitten3.jpg', 'kitten4.jpg'] + } + }, + { + user: 'foobar', + post: { + text: "here's an animated kitten gif", + media: ['kitten1.gif'] + } + }, + { + user: 'foobar', + post: { + text: "here's a secret animated kitten gif", + media: ['kitten2.gif'], + sensitive: true + } + }, + { + user: 'foobar', + post: { + text: "content warning", + spoiler: 'CW' + } + }, + { + user: 'foobar', + post: { + text: "here's a video", + media: ['kitten1.mp4'] + } + }, + { + user: 'foobar', + post: { + text: "here's a secret video", + media: ['kitten2.mp4'] + } + }, + { + user: 'foobar', + post: { + text: "here's a kitten with a CW", + media: ['kitten5.jpg'], + sensitive: true, + spoiler: 'kitten CW' + } + }, + // notifications for foobar + { + user: 'admin', + follow: 'foobar' + }, + { + user: 'admin', + post: { + text: '@foobar hello foobar', + privacy: 'unlisted' + } + }, + { + user: 'quux', + follow: 'foobar' + }, + { + user: 'admin', + post: { + internalId: 3, + text: '@foobar notification of direct message', + privacy: 'direct' + } + }, + { + user: 'admin', + favorite: 3 + }, + { + user: 'admin', + post: { + internalId: 4, + text: '@foobar notification of followers-only message', + privacy: 'private' + } + }, + { + user: 'admin', + favorite: 4 + }, + { + user: 'admin', + post: { + internalId: 1, + text: '@foobar notification of unlisted message', + privacy: 'unlisted' + } + }, + { + user: 'admin', + boost: 1 + }, + { + user: 'foobar', + post: { + internalId: 2, + text: 'this is unlisted', + privacy: 'private' + } + }, + { + user: 'admin', + boost: 2 + }, + { + user: 'admin', + favorite: 2 + }, + { + user: 'quux', + post: { + internalId: 5, + text: 'pinned toot 1', + privacy: 'private' + } + }, + { + user: 'quux', + post: { + internalId: 6, + text: 'pinned toot 2', + privacy: 'private' + } + } +]).concat(times(25, i => ({ + user: 'quux', + post: { + internalId: 100 + i, + text: 'unlisted thread ' + (i + 1), + privacy: 'private', + inReplyTo: i > 0 && (100 + i) + } +}))).concat([ + { + user: 'quux', + pin: 5 + }, + { + user: 'quux', + pin: 6 + }, + { + user: 'admin', + boost: 5 + }, + { + user: 'admin', + favorite: 5 + }, + { + user: 'foobar', + favorite: 5 + }, + { + user: 'admin', + favorite: 6 + }, + { + user: 'ExternalLinks', + post: { + text: 'here are some hashtags: #kitten #kitties', + privacy: 'private' + } + }, + { + user: 'ExternalLinks', + post: { + text: 'here are some external links: https://joinmastodon.org https://github.com/tootsuite/mastodon', + privacy: 'private' + } + }, + { + user: 'ExternalLinks', + post: { + text: 'here are some users: @admin @quux', + privacy: 'private' + } + } +]) \ No newline at end of file diff --git a/bin/restore-mastodon-data.js b/bin/restore-mastodon-data.js index 0412a5c..f4710c9 100644 --- a/bin/restore-mastodon-data.js +++ b/bin/restore-mastodon-data.js @@ -1,226 +1,44 @@ -const times = require('lodash/times') +import { actions } from './mastodon-data' +import { users } from '../tests/users' +import { postStatus } from '../routes/_api/statuses' +import { uploadMedia } from '../routes/_api/media' +import { followAccount } from '../routes/_api/follow' +import { favoriteStatus } from '../routes/_api/favorite' +import { reblogStatus } from '../routes/_api/reblog' -const actions = times(30, i => ({ - post: { - text: (i + 1) - }, - user: 'admin' -})).concat([ - { - user: 'foobar', - post: { - text: 'hello world' - } - }, - { - user: 'foobar', - post: { - text: "here's a kitten", - media: ['kitten1.jpg'] - } - }, - { - user: 'foobar', - post: { - text: "here's a secret kitten", - media: ['kitten2.jpg'], - sensitive: true - } - }, - { - user: 'foobar', - post: { - text: "here's 2 kitten photos", - media: ['kitten3.jpg', 'kitten4.jpg'] - } - }, - { - user: 'foobar', - post: { - text: "here's an animated kitten gif", - media: ['kitten1.gif'] - } - }, - { - user: 'foobar', - post: { - text: "here's a secret animated kitten gif", - media: ['kitten2.gif'], - sensitive: true - } - }, - { - user: 'foobar', - post: { - text: "content warning", - spoiler: 'CW' - } - }, - { - user: 'foobar', - post: { - text: "here's a video", - media: ['kitten1.mp4'] - } - }, - { - user: 'foobar', - post: { - text: "here's a secret video", - media: ['kitten2.mp4'] - } - }, - { - user: 'foobar', - post: { - text: "here's a kitten with a CW", - media: ['kitten5.jpg'], - sensitive: true, - spoiler: 'kitten CW' - } - }, - // notifications for foobar - { - user: 'admin', - follow: 'foobar' - }, - { - user: 'admin', - post: { - text: '@foobar hello foobar', - privacy: 'unlisted' - } - }, - { - user: 'quux', - follow: 'foobar' - }, - { - user: 'admin', - post: { - internalId: 3, - text: '@foobar notification of direct message', - privacy: 'direct' - } - }, - { - user: 'admin', - favorite: 3 - }, - { - user: 'admin', - post: { - internalId: 4, - text: '@foobar notification of followers-only message', - privacy: 'private' - } - }, - { - user: 'admin', - favorite: 4 - }, - { - user: 'admin', - post: { - internalId: 1, - text: '@foobar notification of unlisted message', - privacy: 'unlisted' - } - }, - { - user: 'admin', - boost: 1 - }, - { - user: 'foobar', - post: { - internalId: 2, - text: 'this is unlisted', - privacy: 'private' - } - }, - { - user: 'admin', - boost: 2 - }, - { - user: 'admin', - favorite: 2 - }, - { - user: 'quux', - post: { - internalId: 5, - text: 'pinned toot 1', - privacy: 'private' - } - }, - { - user: 'quux', - post: { - internalId: 6, - text: 'pinned toot 2', - privacy: 'private' - } - } -]).concat(times(25, i => ({ - user: 'quux', - post: { - internalId: 100 + i, - text: 'unlisted thread ' + (i + 1), - privacy: 'private', - inReplyTo: i > 0 && (100 + i) - } -}))).concat([ - { - user: 'quux', - pin: 5 - }, - { - user: 'quux', - pin: 6 - }, - { - user: 'admin', - boost: 5 - }, - { - user: 'admin', - favorite: 5 - }, - { - user: 'foobar', - favorite: 5 - }, - { - user: 'admin', - favorite: 6 - }, - { - user: 'ExternalLinks', - post: { - text: 'here are some hashtags: #kitten #kitties', - privacy: 'private' - } - }, - { - user: 'ExternalLinks', - post: { - text: 'here are some external links: https://joinmastodon.org https://github.com/tootsuite/mastodon', - privacy: 'private' - } - }, - { - user: 'ExternalLinks', - post: { - text: 'here are some users: @admin @quux', - privacy: 'private' - } - } -]) +import path from 'path' +global.File = require('file-api').File +global.FormData = require('file-api').FormData +global.fetch = require('node-fetch') async function restoreMastodonData () { console.log('Restoring mastodon data...') + let internalIdsToIds = {} + for (let action of actions) { + let accessToken = users[action.user].accessToken + if (action.post) { + let { text, media, sensitive, spoiler, privacy, inReplyTo, internalId } = action.post + if (typeof inReplyTo !== 'undefined') { + inReplyTo = internalIdsToIds[inReplyTo] + } + let mediaIds = media && await Promise.all(media.map(async mediaItem => { + let file = new File(path.join(__dirname, '../tests/images/' + mediaItem)) + let mediaResponse = await uploadMedia('localhost:3000', accessToken, file) + return mediaResponse.id + })) + let status = await postStatus('localhost:3000', accessToken, text, inReplyTo, mediaIds, + sensitive, spoiler, privacy || 'public') + if (typeof internalId !== 'undefined') { + internalIdsToIds[internalId] = status.id + } + } else if (action.follow) { + await followAccount('localhost:3000', accessToken, action.follow) + } else if (action.favorite) { + await favoriteStatus('localhost:3000', accessToken, internalIdsToIds[action.favorite]) + } else if (action.boost) { + await reblogStatus('localhost:3000', accessToken, internalIdsToIds[action.favorite]) + } + } } module.exports = restoreMastodonData \ No newline at end of file diff --git a/bin/run-mastodon.js b/bin/run-mastodon.js index 66f6b56..fc3bfb7 100644 --- a/bin/run-mastodon.js +++ b/bin/run-mastodon.js @@ -1,4 +1,4 @@ -const restoreMastodonData = require ('./restore-mastodon-data') +const restoreMastodonData = require('./restore-mastodon-data') const pify = require('pify') const exec = require('child-process-promise').exec const spawn = require('child-process-promise').spawn @@ -71,7 +71,7 @@ async function runMastodon () { async function main () { await cloneMastodon() - await setupMastodonDatabase() + //await setupMastodonDatabase() await runMastodon() //await restoreMastodonData() } diff --git a/fixtures/dump.sql b/fixtures/dump.sql index 36a7388..af20a66 100644 Binary files a/fixtures/dump.sql and b/fixtures/dump.sql differ diff --git a/fixtures/system.tgz b/fixtures/system.tgz index aa84139..f14854d 100644 Binary files a/fixtures/system.tgz and b/fixtures/system.tgz differ diff --git a/package-lock.json b/package-lock.json index 1857aba..03b0197 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,11 @@ "resolved": "https://registry.npmjs.org/@gamestdio/websocket/-/websocket-0.2.2.tgz", "integrity": "sha512-ptWTKBq6IliC7lNhfAI/YI5WBjhPClflZV61V7In/qxGbgrWrlny6/Df4NtrUs7QUeNccv8yqQwjjw+f0veGjQ==" }, + "@std/esm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@std/esm/-/esm-0.25.0.tgz", + "integrity": "sha512-htyUY4TrUSMYLdtmk2RPrNJJGZPAhliYcjf5vIh+RMVEI6Yy88Jl98cWcJY8McPcrizkXdbzOGFtp6pf8qd45w==" + }, "@types/chalk": { "version": "0.4.31", "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-0.4.31.tgz", @@ -34,6 +39,19 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-9.4.0.tgz", "integrity": "sha512-zkYho6/4wZyX6o9UQ8rd0ReEaiEYNNCqYFIAACe2Tf9DrYlgzWW27OigYHnnztnnZQwVRpwWmZKegFmDpinIsA==" }, + "File": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/File/-/File-0.10.2.tgz", + "integrity": "sha1-6Jn3dtJz4iQ7qGEFuzsFbQ+5VgQ=", + "requires": { + "mime": "1.4.1" + } + }, + "FileList": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/FileList/-/FileList-0.10.2.tgz", + "integrity": "sha1-YAOxqXFZNBZLZ8Q0rWqHQaHNFHo=" + }, "a11y-dialog": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/a11y-dialog/-/a11y-dialog-4.0.1.tgz", @@ -1351,6 +1369,11 @@ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, + "bufferjs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bufferjs/-/bufferjs-3.0.1.tgz", + "integrity": "sha1-BpLoKcsQoQVQ5kc5CwNesGw46O8=" + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -3261,6 +3284,21 @@ "object-assign": "4.1.1" } }, + "file-api": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/file-api/-/file-api-0.10.4.tgz", + "integrity": "sha1-LxASJttyfMAXKg3WiPL2iD1SiD0=", + "requires": { + "File": "0.10.2", + "FileList": "0.10.2", + "bufferjs": "3.0.1", + "file-error": "0.10.2", + "filereader": "0.10.3", + "formdata": "0.10.4", + "mime": "1.4.1", + "remedial": "1.0.7" + } + }, "file-entry-cache": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", @@ -3270,11 +3308,21 @@ "object-assign": "4.1.1" } }, + "file-error": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/file-error/-/file-error-0.10.2.tgz", + "integrity": "sha1-ljtIuSc7PUuEtADuVxvHixc5cko=" + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" }, + "filereader": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/filereader/-/filereader-0.10.3.tgz", + "integrity": "sha1-x0fUos2PYeVBinwH/hJXpD8KzbE=" + }, "filesize": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.0.tgz", @@ -3467,6 +3515,11 @@ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, + "foreachasync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", + "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=" + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -3482,6 +3535,26 @@ "mime-types": "2.1.17" } }, + "formdata": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/formdata/-/formdata-0.10.4.tgz", + "integrity": "sha1-liH9wMw2H0oBEd5dJbNfanjcVaA=", + "requires": { + "File": "0.10.2", + "FileList": "0.10.2", + "bufferjs": "2.0.0", + "filereader": "0.10.3", + "foreachasync": "3.0.0", + "remedial": "1.0.7" + }, + "dependencies": { + "bufferjs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bufferjs/-/bufferjs-2.0.0.tgz", + "integrity": "sha1-aF5x7VwGAOPXA/+b0BK7MnCjnig=" + } + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -7805,6 +7878,11 @@ "isobject": "2.1.0" } }, + "remedial": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.7.tgz", + "integrity": "sha1-1mdEE6ZWdgB74A3UAJgJh7LDAME=" + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", diff --git a/package.json b/package.json index 014892e..02cf66b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "build-svg": "node ./bin/build-svg.js", "build-sass": "node ./bin/build-sass.js", "build-sass-watch": "node ./bin/build-sass.js --watch", - "run-mastodon": "node ./bin/run-mastodon", + "run-mastodon": "node -r @std/esm ./bin/run-mastodon", "run-testcafe": "cross-env-shell testcafe --hostname localhost --skip-js-errors $BROWSER tests/spec", "test": "cross-env BROWSER=chrome:headless npm run test-browser", "test-browser": "run-p --race run-mastodon dev test-mastodon", @@ -24,6 +24,7 @@ }, "dependencies": { "@gamestdio/websocket": "^0.2.2", + "@std/esm": "^0.25.0", "a11y-dialog": "^4.0.1", "cheerio": "^1.0.0-rc.2", "child-process-promise": "^2.2.1", @@ -34,6 +35,7 @@ "express": "^4.16.2", "extract-text-webpack-plugin": "^3.0.2", "fg-loadcss": "^2.0.1", + "file-api": "^0.10.4", "font-awesome-svg-png": "^1.2.2", "glob": "^7.1.2", "idb-keyval": "^2.3.0", @@ -105,5 +107,8 @@ "dist", "routes/_utils/asyncModules.js" ] + }, + "@std/esm": { + "mode": "js" } } diff --git a/routes/_api/follow.js b/routes/_api/follow.js new file mode 100644 index 0000000..b093101 --- /dev/null +++ b/routes/_api/follow.js @@ -0,0 +1,12 @@ +import { postWithTimeout } from '../_utils/ajax' +import { auth, basename } from './utils' + +export async function followAccount(instanceName, accessToken, accountId) { + let url = `${basename(instanceName)}/api/v1/accounts/${accountId}/follow` + return postWithTimeout(url, null, auth(accessToken)) +} + +export async function unfollowAccount(instanceName, accessToken, accountId) { + let url = `${basename(instanceName)}/api/v1/accounts/${accountId}/unfollow` + return postWithTimeout(url, null, auth(accessToken)) +} \ No newline at end of file diff --git a/tests/images/kitten1.gif b/tests/images/kitten1.gif new file mode 100644 index 0000000..6968a6b Binary files /dev/null and b/tests/images/kitten1.gif differ diff --git a/tests/images/kitten1.mp4 b/tests/images/kitten1.mp4 new file mode 100644 index 0000000..be4d2ed Binary files /dev/null and b/tests/images/kitten1.mp4 differ diff --git a/tests/images/kitten2.gif b/tests/images/kitten2.gif new file mode 100644 index 0000000..da6ccc9 Binary files /dev/null and b/tests/images/kitten2.gif differ diff --git a/tests/images/kitten2.mp4 b/tests/images/kitten2.mp4 new file mode 100644 index 0000000..ec716c7 Binary files /dev/null and b/tests/images/kitten2.mp4 differ diff --git a/tests/images/kitten3.jpg b/tests/images/kitten3.jpg index 6740d7c..3446e77 100644 Binary files a/tests/images/kitten3.jpg and b/tests/images/kitten3.jpg differ diff --git a/tests/images/kitten4.jpg b/tests/images/kitten4.jpg index e4f6b36..03f925a 100644 Binary files a/tests/images/kitten4.jpg and b/tests/images/kitten4.jpg differ diff --git a/tests/serverActions.js b/tests/serverActions.js index 742b346..aee0e9c 100644 --- a/tests/serverActions.js +++ b/tests/serverActions.js @@ -2,8 +2,8 @@ import { favoriteStatus } from '../routes/_api/favorite' import fetch from 'node-fetch' global.fetch = fetch -const accessToken = 'a306d698185db24b12385a5432817551d7ac94bdcbe23233d4e5eff70f6408c4' +import { users } from './users' export async function favoriteStatusAsAdmin (statusId) { - return favoriteStatus('localhost:3000', accessToken, statusId) + return favoriteStatus('localhost:3000', users.admin.accessToken, statusId) } diff --git a/tests/users.js b/tests/users.js new file mode 100644 index 0000000..b134b91 --- /dev/null +++ b/tests/users.js @@ -0,0 +1,22 @@ +export const users = { + admin: { + username: 'admin', + password: 'mastodonadmin', + accessToken: '1a62cc93aa6ccdad5a1048bd37b627c9df72f68c7f4b87d2daaeb83010ff44ab' + }, + foobar: { + username: 'foobar', + password: 'foobarfoobar', + accessToken: '67868cd5b1f1279697ffffdb44c144b0ae427d74f4540717b67bf4a9c5c5c346' + }, + quux: { + username: 'quux', + password: 'quuxquuxquux', + accessToken: 'd100083c1749392da7e1c12e63a6fba75f15ea1fbce93361d722889abf418464' + }, + ExternalLinks: { + username: 'ExternalLinks', + password: 'ExternalLinksExternalLink', + accessToken: 'e9a463ba1729ae0049a97a312af702cb3d08d84de1cc8d6da3fad90af068117b' + } +} \ No newline at end of file