mirror of https://gitgud.io/fatchan/jschan.git
commit
986e0c89bd
53 changed files with 12379 additions and 3536 deletions
@ -0,0 +1,8 @@ |
|||||||
|
node_modules/ |
||||||
|
static/ |
||||||
|
docker/jschan/Dockerfile |
||||||
|
docker/jschan/Dockerfile-reset |
||||||
|
docker/nginx/Dockerfile |
||||||
|
docker/static/ |
||||||
|
tools/ |
||||||
|
gulp/res/js/socket.io.js |
@ -0,0 +1,70 @@ |
|||||||
|
version: "3.5" |
||||||
|
services: |
||||||
|
|
||||||
|
redis: |
||||||
|
command: redis-server --requirepass changeme |
||||||
|
image: redis:alpine |
||||||
|
|
||||||
|
mongodb: |
||||||
|
image: mongo:latest |
||||||
|
environment: |
||||||
|
- MONGO_INITDB_ROOT_USERNAME=jschan |
||||||
|
- MONGO_INITDB_ROOT_PASSWORD=changeme |
||||||
|
|
||||||
|
nginx: |
||||||
|
build: |
||||||
|
context: . |
||||||
|
dockerfile: ./docker/nginx/Dockerfile |
||||||
|
args: |
||||||
|
ENABLED_MODULES: geoip |
||||||
|
ports: |
||||||
|
- "80:80" |
||||||
|
volumes: |
||||||
|
- ./docker/static/:/path/to/jschan/static/ |
||||||
|
depends_on: |
||||||
|
- jschan |
||||||
|
|
||||||
|
jschan: |
||||||
|
build: |
||||||
|
context: . |
||||||
|
dockerfile: ./docker/jschan/Dockerfile |
||||||
|
network: jschan_default |
||||||
|
environment: |
||||||
|
- NODE_ENV=development |
||||||
|
- JSCHAN_IP=0.0.0.0 |
||||||
|
- NO_CAPTCHA=1 |
||||||
|
- MONGO_USERNAME=jschan |
||||||
|
- MONGO_PASSWORD=changeme |
||||||
|
- REDIS_PASSWORD=changeme |
||||||
|
- COOKIE_SECRET=changeme |
||||||
|
- TRIPCODE_SECRET=changeme |
||||||
|
- IP_HASH_SECRET=changeme |
||||||
|
- POST_PASSWORD_SECRET=changeme |
||||||
|
- GOOGLE_SITEKEY=changeme |
||||||
|
- GOOGLE_SECRETKEY=changeme |
||||||
|
- HCAPTCHA_SITEKEY=10000000-ffff-ffff-ffff-000000000001 |
||||||
|
- HCAPTCHA_SECRETKEY=0x0000000000000000000000000000000000000000 |
||||||
|
volumes: |
||||||
|
- ./docker/static:/opt/static/ |
||||||
|
depends_on: |
||||||
|
- redis |
||||||
|
- mongodb |
||||||
|
|
||||||
|
jschan-reset: |
||||||
|
build: |
||||||
|
context: . |
||||||
|
dockerfile: ./docker/jschan/Dockerfile-reset |
||||||
|
network: jschan_default |
||||||
|
environment: |
||||||
|
- MONGO_USERNAME=jschan |
||||||
|
- MONGO_PASSWORD=changeme |
||||||
|
- REDIS_PASSWORD=changeme |
||||||
|
volumes: |
||||||
|
- ./docker/static:/opt/static/ |
||||||
|
depends_on: |
||||||
|
- redis |
||||||
|
- mongodb |
||||||
|
|
||||||
|
networks: |
||||||
|
default: |
||||||
|
name: jschan_default |
@ -0,0 +1,23 @@ |
|||||||
|
FROM node:16 |
||||||
|
|
||||||
|
RUN apt-get update -y |
||||||
|
RUN apt-get install ffmpeg imagemagick graphicsmagick -y |
||||||
|
|
||||||
|
WORKDIR /opt |
||||||
|
|
||||||
|
COPY . . |
||||||
|
|
||||||
|
RUN npm install |
||||||
|
|
||||||
|
RUN npm install -g pm2 gulp |
||||||
|
|
||||||
|
COPY ./docker/jschan/secrets.js ./configs/secrets.js |
||||||
|
|
||||||
|
#i fucking hate docker |
||||||
|
ENV MONGO_USERNAME jschan |
||||||
|
ENV MONGO_PASSWORD changeme |
||||||
|
ENV REDIS_PASSWORD changeme |
||||||
|
|
||||||
|
RUN gulp generate-favicon |
||||||
|
|
||||||
|
CMD ["/bin/sh", "-c", "gulp; pm2-runtime start ecosystem.config.js"] |
@ -0,0 +1,20 @@ |
|||||||
|
FROM node:16 |
||||||
|
|
||||||
|
WORKDIR /opt |
||||||
|
|
||||||
|
COPY . . |
||||||
|
|
||||||
|
RUN npm install |
||||||
|
|
||||||
|
RUN npm i -g pm2 gulp |
||||||
|
|
||||||
|
COPY ./docker/jschan/secrets.js ./configs/secrets.js |
||||||
|
|
||||||
|
#i fucking hate docker |
||||||
|
ENV MONGO_USERNAME jschan |
||||||
|
ENV MONGO_PASSWORD changeme |
||||||
|
ENV REDIS_PASSWORD changeme |
||||||
|
|
||||||
|
RUN gulp generate-favicon |
||||||
|
|
||||||
|
CMD ["/bin/sh", "-c", "gulp reset; gulp"] |
@ -0,0 +1,40 @@ |
|||||||
|
module.exports = { |
||||||
|
|
||||||
|
//mongodb connection string
|
||||||
|
dbURL: `mongodb://${process.env.MONGO_USERNAME}:${process.env.MONGO_PASSWORD}@mongodb:27017`, |
||||||
|
|
||||||
|
//database name
|
||||||
|
dbName: 'jschan', |
||||||
|
|
||||||
|
//redis connection info
|
||||||
|
redis: { |
||||||
|
host: 'redis', |
||||||
|
port: '6379', |
||||||
|
password: process.env.REDIS_PASSWORD, |
||||||
|
}, |
||||||
|
|
||||||
|
//backend webserver port
|
||||||
|
port: 7000, |
||||||
|
|
||||||
|
//secrets/salts for various things
|
||||||
|
cookieSecret: process.env.COOKIE_SECRET, |
||||||
|
tripcodeSecret: process.env.TRIPCODE_SECRET, |
||||||
|
ipHashSecret: process.env.IP_HASH_SECRET, |
||||||
|
postPasswordSecret: process.env.POST_PASSWORD_SECRET, |
||||||
|
|
||||||
|
//keys for google recaptcha
|
||||||
|
google: { |
||||||
|
siteKey: process.env.GOOGLE_SITEKEY, |
||||||
|
secretKey: process.env.GOOGLE_SECRETKEY, |
||||||
|
}, |
||||||
|
|
||||||
|
//keys for hcaptcha
|
||||||
|
hcaptcha: { |
||||||
|
siteKey: process.env.HCAPTCHA_SITEKEY, |
||||||
|
secretKey: process.env.HCAPTCHA_SECRETKEY, |
||||||
|
}, |
||||||
|
|
||||||
|
//enable debug logging
|
||||||
|
debugLogs: true, |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,88 @@ |
|||||||
|
FROM nginx:mainline as builder |
||||||
|
|
||||||
|
ARG ENABLED_MODULES |
||||||
|
|
||||||
|
RUN set -ex \ |
||||||
|
&& if [ "$ENABLED_MODULES" = "" ]; then \ |
||||||
|
echo "No additional modules enabled, exiting"; \ |
||||||
|
exit 1; \ |
||||||
|
fi |
||||||
|
|
||||||
|
#COPY ./ /modules/ |
||||||
|
|
||||||
|
RUN set -ex \ |
||||||
|
&& apt update \ |
||||||
|
&& apt install -y --no-install-suggests --no-install-recommends \ |
||||||
|
patch make wget mercurial devscripts debhelper dpkg-dev \ |
||||||
|
quilt lsb-release build-essential libxml2-utils xsltproc \ |
||||||
|
equivs git g++ \ |
||||||
|
&& hg clone -r ${NGINX_VERSION}-${PKG_RELEASE%%~*} https://hg.nginx.org/pkg-oss/ \ |
||||||
|
&& cd pkg-oss \ |
||||||
|
&& mkdir /tmp/packages \ |
||||||
|
&& for module in $ENABLED_MODULES; do \ |
||||||
|
echo "Building $module for nginx-$NGINX_VERSION"; \ |
||||||
|
if [ -d /modules/$module ]; then \ |
||||||
|
echo "Building $module from user-supplied sources"; \ |
||||||
|
# check if module sources file is there and not empty |
||||||
|
if [ ! -s /modules/$module/source ]; then \ |
||||||
|
echo "No source file for $module in modules/$module/source, exiting"; \ |
||||||
|
exit 1; \ |
||||||
|
fi; \ |
||||||
|
# some modules require build dependencies |
||||||
|
if [ -f /modules/$module/build-deps ]; then \ |
||||||
|
echo "Installing $module build dependencies"; \ |
||||||
|
apt update && apt install -y --no-install-suggests --no-install-recommends $(cat /modules/$module/build-deps | xargs); \ |
||||||
|
fi; \ |
||||||
|
# if a module has a build dependency that is not in a distro, provide a |
||||||
|
# shell script to fetch/build/install those |
||||||
|
# note that shared libraries produced as a result of this script will |
||||||
|
# not be copied from the builder image to the main one so build static |
||||||
|
if [ -x /modules/$module/prebuild ]; then \ |
||||||
|
echo "Running prebuild script for $module"; \ |
||||||
|
/modules/$module/prebuild; \ |
||||||
|
fi; \ |
||||||
|
/pkg-oss/build_module.sh -v $NGINX_VERSION -f -y -o /tmp/packages -n $module $(cat /modules/$module/source); \ |
||||||
|
BUILT_MODULES="$BUILT_MODULES $(echo $module | tr '[A-Z]' '[a-z]' | tr -d '[/_\-\.\t ]')"; \ |
||||||
|
elif make -C /pkg-oss/debian list | grep -P "^$module\s+\d" > /dev/null; then \ |
||||||
|
echo "Building $module from pkg-oss sources"; \ |
||||||
|
cd /pkg-oss/debian; \ |
||||||
|
make rules-module-$module BASE_VERSION=$NGINX_VERSION NGINX_VERSION=$NGINX_VERSION; \ |
||||||
|
mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" debuild-module-$module/nginx-$NGINX_VERSION/debian/control; \ |
||||||
|
make module-$module BASE_VERSION=$NGINX_VERSION NGINX_VERSION=$NGINX_VERSION; \ |
||||||
|
find ../../ -maxdepth 1 -mindepth 1 -type f -name "*.deb" -exec mv -v {} /tmp/packages/ \;; \ |
||||||
|
BUILT_MODULES="$BUILT_MODULES $module"; \ |
||||||
|
else \ |
||||||
|
echo "Don't know how to build $module module, exiting"; \ |
||||||
|
exit 1; \ |
||||||
|
fi; \ |
||||||
|
done \ |
||||||
|
&& echo "BUILT_MODULES=\"$BUILT_MODULES\"" > /tmp/packages/modules.env |
||||||
|
|
||||||
|
FROM nginx:mainline |
||||||
|
COPY --from=builder /tmp/packages /tmp/packages |
||||||
|
RUN set -ex \ |
||||||
|
&& apt update \ |
||||||
|
&& apt-get install wget -y \ |
||||||
|
&& . /tmp/packages/modules.env \ |
||||||
|
&& for module in $BUILT_MODULES; do \ |
||||||
|
apt install --no-install-suggests --no-install-recommends -y /tmp/packages/nginx-module-${module}_${NGINX_VERSION}*.deb; \ |
||||||
|
done \ |
||||||
|
&& rm -rf /tmp/packages \ |
||||||
|
&& rm -rf /var/lib/apt/lists/ |
||||||
|
|
||||||
|
RUN mkdir /usr/share/GeoIP |
||||||
|
RUN wget https://dl.miyuru.lk/geoip/dbip/country/dbip.dat.gz |
||||||
|
RUN gunzip dbip.dat.gz |
||||||
|
RUN mv dbip.dat /usr/share/GeoIP/GeoIP.dat |
||||||
|
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf |
||||||
|
|
||||||
|
COPY ./docker/nginx/nginx.conf /etc/nginx/nginx.conf |
||||||
|
|
||||||
|
COPY ./docker/nginx/jschan.conf /etc/nginx/conf.d/ |
||||||
|
|
||||||
|
COPY ./configs/nginx/snippets/ /etc/nginx/snippets/ |
||||||
|
|
||||||
|
EXPOSE 80 |
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"] |
@ -0,0 +1,16 @@ |
|||||||
|
upstream chan { |
||||||
|
server jschan:7000; |
||||||
|
} |
||||||
|
|
||||||
|
server { |
||||||
|
server_name _; |
||||||
|
client_max_body_size 0; |
||||||
|
|
||||||
|
listen 80; |
||||||
|
listen [::]:80; |
||||||
|
|
||||||
|
include /etc/nginx/snippets/security_headers.conf; |
||||||
|
include /etc/nginx/snippets/error_pages.conf; |
||||||
|
include /etc/nginx/snippets/jschan_clearnet_routes.conf; |
||||||
|
include /etc/nginx/snippets/jschan_common_routes.conf; |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
load_module /etc/nginx/modules/ngx_http_geoip_module-debug.so; |
||||||
|
|
||||||
|
worker_processes auto; |
||||||
|
pid /run/nginx.pid; |
||||||
|
|
||||||
|
events { |
||||||
|
worker_connections 1000; |
||||||
|
} |
||||||
|
|
||||||
|
http { |
||||||
|
include /etc/nginx/mime.types; |
||||||
|
default_type application/octet-stream; |
||||||
|
geoip_country /usr/share/GeoIP/GeoIP.dat; |
||||||
|
|
||||||
|
map_hash_max_size 4096; |
||||||
|
map_hash_bucket_size 256; |
||||||
|
|
||||||
|
aio threads; |
||||||
|
sendfile on; |
||||||
|
tcp_nopush on; |
||||||
|
tcp_nodelay on; |
||||||
|
server_tokens off; |
||||||
|
types_hash_max_size 2048; |
||||||
|
server_names_hash_bucket_size 128; |
||||||
|
client_max_body_size 0; |
||||||
|
|
||||||
|
#proxy_request_buffering off; |
||||||
|
|
||||||
|
log_format custom '[$time_local] $remote_addr $status "$request" "$http_referer" "$http_user_agent" $bytes_sent'; |
||||||
|
access_log /var/log/nginx/access.log custom; |
||||||
|
error_log /var/log/nginx/error.log; |
||||||
|
|
||||||
|
gzip on; |
||||||
|
#gzip_vary off; |
||||||
|
gzip_comp_level 6; |
||||||
|
gzip_proxied any; |
||||||
|
gzip_types text/plain text/css text/js text/xml text/javascript image/x-icon application/javascript application/json application/xml application/rss+xml image/svg+xml; |
||||||
|
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE |
||||||
|
ssl_prefer_server_ciphers on; |
||||||
|
|
||||||
|
include /etc/nginx/conf.d/*; |
||||||
|
|
||||||
|
} |
@ -1,12 +0,0 @@ |
|||||||
'use strict'; |
|
||||||
|
|
||||||
//https://stackoverflow.com/a/4413721
|
|
||||||
module.exports = (startDate, stopDate) => { |
|
||||||
const dateArray = new Array(); |
|
||||||
let currentDate = startDate; |
|
||||||
while (currentDate <= stopDate) { |
|
||||||
dateArray.push(new Date (currentDate.valueOf())); |
|
||||||
currentDate.setDate(currentDate.getDate() + 1); |
|
||||||
} |
|
||||||
return dateArray; |
|
||||||
} |
|
@ -0,0 +1,25 @@ |
|||||||
|
const decodeQueryIp = require('./decodequeryip.js'); |
||||||
|
const Permission = require('./permission.js'); |
||||||
|
const Permissions = require('./permissions.js'); |
||||||
|
const ROOT = new Permission(); |
||||||
|
ROOT.setAll(Permission.allPermissions); |
||||||
|
const NO_PERMISSION = new Permission(); |
||||||
|
|
||||||
|
describe('decode query ip', () => { |
||||||
|
|
||||||
|
const cases = [ |
||||||
|
{ in: { query: null, permission: ROOT }, out: null }, |
||||||
|
{ in: { query: {}, permission: ROOT }, out: null }, |
||||||
|
{ in: { query: { ip: '10.0.0.1' }, permission: ROOT }, out: '10.0.0.1' }, |
||||||
|
{ in: { query: { ip: '10.0.0.1' }, permission: NO_PERMISSION }, out: null }, |
||||||
|
{ in: { query: { ip: '8s7AGX4n.qHsw9mp.uw54Nfl.IP' }, permission: ROOT }, out: '8s7AGX4n.qHsw9mp.uw54Nfl.IP' }, |
||||||
|
{ in: { query: { ip: '8s7AGX4n.qHsw9mp.uw54Nfl.IP' }, permission: NO_PERMISSION }, out: '8s7AGX4n.qHsw9mp.uw54Nfl.IP' }, |
||||||
|
]; |
||||||
|
|
||||||
|
for(let i in cases) { |
||||||
|
test(`should output ${cases[i].out} for an input of ${cases[i].in}`, () => { |
||||||
|
expect(decodeQueryIp(cases[i].in.query, cases[i].in.permission)).toStrictEqual(cases[i].out) |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,17 @@ |
|||||||
|
const escapeRegExp = require('./escaperegexp.js'); |
||||||
|
|
||||||
|
describe('escape regular expression', () => { |
||||||
|
|
||||||
|
const cases = [ |
||||||
|
{ in: '', out: '' }, |
||||||
|
{ in: '/', out: '/' }, |
||||||
|
{ in: '.*+?^${}()|[]\\', out: '\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\\\' }, |
||||||
|
]; |
||||||
|
|
||||||
|
for(let i in cases) { |
||||||
|
test(`should output ${cases[i].out} for an input of ${cases[i].in}`, () => { |
||||||
|
expect(escapeRegExp(cases[i].in)).toStrictEqual(cases[i].out) |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,19 @@ |
|||||||
|
const formatSize = require('./formatsize.js'); |
||||||
|
|
||||||
|
describe('formatSize() - convert bytes to human readable file size', () => { |
||||||
|
const cases = [ |
||||||
|
{in: 1024, out: "1KB"}, |
||||||
|
{in: Math.pow(1024, 2), out: "1MB"}, |
||||||
|
{in: Math.pow(1024, 3), out: "1GB"}, |
||||||
|
{in: Math.pow(1024, 4), out: "1TB"}, |
||||||
|
{in: Math.pow(1024, 5), out: "1024TB"}, |
||||||
|
{in: Math.pow(1024, 3)+(Math.pow(1024, 2)*512), out: "1.5GB"}, |
||||||
|
{in: 100, out: "100B"}, |
||||||
|
{in: 0, out: "0B"}, |
||||||
|
]; |
||||||
|
for(let i in cases) { |
||||||
|
test(`should output ${cases[i].out} for an input of ${cases[i].in} bytes`, () => { |
||||||
|
expect(formatSize(cases[i].in)).toBe(cases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,23 @@ |
|||||||
|
const pageQueryConverter = require('./pagequeryconverter.js'); |
||||||
|
const limit = 30; |
||||||
|
|
||||||
|
describe('page query converter', () => { |
||||||
|
const cases = [ |
||||||
|
{ in: null, out: { offset: 0, "queryString": "", page: 1 } }, |
||||||
|
{ in: { }, out: { offset: 0, "queryString": "", page: 1 } }, |
||||||
|
{ in: { page: [1, 2, 3] }, out: { offset: 0, "queryString": "", page: 1 } }, |
||||||
|
{ in: { page: "test" }, out: { offset: 0, "queryString": "", page: 1 } }, |
||||||
|
{ in: { page: null }, out: { offset: 0, "queryString": "", page: 1 } }, |
||||||
|
{ in: { page: -1 }, out: { offset: 0, "queryString": "", page: 1 } }, |
||||||
|
{ in: { page: 0 }, out: { offset: 0, "queryString": "", page: 1 } }, |
||||||
|
{ in: { page: 1 }, out: { offset: 0, "queryString": "", page: 1 } }, |
||||||
|
{ in: { page: 5 }, out: { offset: limit*4, "queryString": "", page: 5 } }, |
||||||
|
{ in: { page: 10, other: "test" }, out: { offset: limit*9, "queryString": "other=test", page: 10 } }, |
||||||
|
{ in: { other: "test" }, out: { offset: 0, "queryString": "other=test", page: 1 } }, |
||||||
|
]; |
||||||
|
for(let i in cases) { |
||||||
|
test(`should contain ${cases[i].out} for an input of ${cases[i].in}`, () => { |
||||||
|
expect(pageQueryConverter(cases[i].in, limit)).toStrictEqual(cases[i].out) |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,41 @@ |
|||||||
|
const paramConverter = require('./paramconverter.js'); |
||||||
|
const { WEEK, DAY, HOUR } = require('./timeutils.js'); |
||||||
|
/* |
||||||
|
const defaultOptions = { |
||||||
|
timeFields: [], |
||||||
|
trimFields: [], |
||||||
|
allowedArrays: [], |
||||||
|
numberFields: [], |
||||||
|
numberArrays: [], |
||||||
|
objectIdParams: [], |
||||||
|
objectIdFields: [], |
||||||
|
objectIdArrays: [], |
||||||
|
processThreadIdParam: false, |
||||||
|
processDateParam: false, |
||||||
|
processMessageLength: false, |
||||||
|
}; |
||||||
|
*/ |
||||||
|
|
||||||
|
describe('paramconverter', () => { |
||||||
|
|
||||||
|
const cases = [ |
||||||
|
{ |
||||||
|
in: { options: { trimFields: ['username', 'password'] }, body: { username: 'trimmed ', password: 'trimmed ' } }, |
||||||
|
out: { username: 'trimmed', password: 'trimmed' } |
||||||
|
}, |
||||||
|
{ |
||||||
|
in: { options: { timeFields: ['test'] }, body: { test: '1w2d3h' } }, |
||||||
|
out: { test: WEEK+(2*DAY)+(3*HOUR) } |
||||||
|
}, |
||||||
|
//todo: add a bunch more
|
||||||
|
]; |
||||||
|
|
||||||
|
for(let i in cases) { |
||||||
|
test(`should output ${cases[i].out} for an input of ${cases[i].in}`, () => { |
||||||
|
const converter = paramConverter(cases[i].in.options); |
||||||
|
converter({ body: cases[i].in.body }, {}, () => {}); |
||||||
|
expect(cases[i].in.body).toStrictEqual(cases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,73 @@ |
|||||||
|
const randomRange = require('./randomrange.js'); |
||||||
|
const Permission = require('./permission.js'); |
||||||
|
const Permissions = require('./permissions.js'); |
||||||
|
|
||||||
|
describe('testing permissions', () => { |
||||||
|
|
||||||
|
const NO_PERMISSION = new Permission(); |
||||||
|
|
||||||
|
const ANON = new Permission(); |
||||||
|
ANON.setAll([ |
||||||
|
Permissions.USE_MARKDOWN_PINKTEXT, Permissions.USE_MARKDOWN_GREENTEXT, Permissions.USE_MARKDOWN_BOLD, |
||||||
|
Permissions.USE_MARKDOWN_UNDERLINE, Permissions.USE_MARKDOWN_STRIKETHROUGH, Permissions.USE_MARKDOWN_TITLE, |
||||||
|
Permissions.USE_MARKDOWN_ITALIC, Permissions.USE_MARKDOWN_SPOILER, Permissions.USE_MARKDOWN_MONO, |
||||||
|
Permissions.USE_MARKDOWN_CODE, Permissions.USE_MARKDOWN_DETECTED, Permissions.USE_MARKDOWN_LINK, |
||||||
|
Permissions.USE_MARKDOWN_DICE, Permissions.USE_MARKDOWN_FORTUNE, Permissions.CREATE_BOARD, |
||||||
|
Permissions.CREATE_ACCOUNT |
||||||
|
]); |
||||||
|
|
||||||
|
test('test a permission they have = true', () => { |
||||||
|
expect(ANON.get(Permissions.CREATE_ACCOUNT)).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('test a permission they dont have = false', () => { |
||||||
|
expect(ANON.get(Permissions.ROOT)).toBe(false); |
||||||
|
}); |
||||||
|
|
||||||
|
const BOARD_STAFF = new Permission(ANON.base64) |
||||||
|
BOARD_STAFF.setAll([ |
||||||
|
Permissions.MANAGE_BOARD_GENERAL, Permissions.MANAGE_BOARD_BANS, Permissions.MANAGE_BOARD_LOGS, |
||||||
|
]); |
||||||
|
const BOARD_OWNER = new Permission(BOARD_STAFF.base64) |
||||||
|
BOARD_OWNER.setAll([ |
||||||
|
Permissions.MANAGE_BOARD_OWNER, Permissions.MANAGE_BOARD_STAFF, Permissions.MANAGE_BOARD_CUSTOMISATION, |
||||||
|
Permissions.MANAGE_BOARD_SETTINGS, |
||||||
|
]); |
||||||
|
|
||||||
|
test('BO has all board perms', () => { |
||||||
|
Permissions._MANAGE_BOARD_BITS.every(b => expect(BOARD_OWNER.get(b)).toBe(true)); |
||||||
|
}); |
||||||
|
|
||||||
|
test('applyInheritance() gives BO all board perms as long as they have Permissions.MANAGE_BOARD_OWNER', () => { |
||||||
|
BOARD_OWNER.setAll(Permissions._MANAGE_BOARD_BITS, false); |
||||||
|
BOARD_OWNER.set(Permissions.MANAGE_BOARD_OWNER); |
||||||
|
BOARD_OWNER.applyInheritance(); |
||||||
|
Permissions._MANAGE_BOARD_BITS.every(b => expect(BOARD_OWNER.get(b)).toBe(true)); |
||||||
|
}); |
||||||
|
|
||||||
|
const GLOBAL_STAFF = new Permission(BOARD_OWNER.base64); |
||||||
|
GLOBAL_STAFF.setAll([ |
||||||
|
Permissions.MANAGE_GLOBAL_GENERAL, Permissions.MANAGE_GLOBAL_BANS, Permissions.MANAGE_GLOBAL_LOGS, Permissions.MANAGE_GLOBAL_NEWS, |
||||||
|
Permissions.MANAGE_GLOBAL_BOARDS, Permissions.MANAGE_GLOBAL_SETTINGS, Permissions.MANAGE_BOARD_OWNER, Permissions.BYPASS_FILTERS, |
||||||
|
Permissions.BYPASS_BANS, Permissions.BYPASS_SPAMCHECK, Permissions.BYPASS_RATELIMITS, |
||||||
|
]); |
||||||
|
const ADMIN = new Permission(GLOBAL_STAFF.base64); |
||||||
|
ADMIN.setAll([ |
||||||
|
Permissions.MANAGE_GLOBAL_ACCOUNTS, Permissions.MANAGE_GLOBAL_ROLES, Permissions.VIEW_RAW_IP, |
||||||
|
]); |
||||||
|
const ROOT = new Permission(); |
||||||
|
ROOT.setAll(Permission.allPermissions); |
||||||
|
|
||||||
|
test('root has all permissions', () => { |
||||||
|
Permission.allPermissions.every(p => expect(ROOT.get(p)).toBe(true)); |
||||||
|
}); |
||||||
|
|
||||||
|
test('applyInheritance() gives ROOT all permissions as long as they have Permissions.ROOT', () => { |
||||||
|
NO_PERMISSION.set(Permissions.ROOT); |
||||||
|
NO_PERMISSION.applyInheritance(); |
||||||
|
Permission.allPermissions.every(b => expect(NO_PERMISSION.get(b)).toBe(true)); |
||||||
|
}); |
||||||
|
|
||||||
|
//todo: what othe rpermissions test should be added?
|
||||||
|
|
||||||
|
}); |
@ -0,0 +1,33 @@ |
|||||||
|
const diceroll = require('./diceroll.js'); |
||||||
|
|
||||||
|
describe('diceroll markdown', () => { |
||||||
|
|
||||||
|
const prepareCases = [ |
||||||
|
{ in: '##3d6', out: '##3d6=' }, |
||||||
|
{ in: '##99d99', out: '##99d99=' }, |
||||||
|
{ in: '##999d999', out: '##999d999' }, |
||||||
|
{ in: '##3d8+5', out: '##3d8+5=' }, |
||||||
|
{ in: '##3d8-5', out: '##3d8-5=' }, |
||||||
|
{ in: '##0d0', out: '##0d0' }, |
||||||
|
]; |
||||||
|
for(let i in prepareCases) { |
||||||
|
test(`should contain ${prepareCases[i].out} for an input of ${prepareCases[i].in}`, () => { |
||||||
|
const output = prepareCases[i].in.replace(diceroll.regexPrepare, diceroll.prepare.bind(null, false)); |
||||||
|
expect(output).toContain(prepareCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const markdownCases = [ |
||||||
|
{ in: '##3d6=10', out: 'Rolled 3 dice with 6 sides =' }, |
||||||
|
{ in: '##99d99=5138', out: 'Rolled 99 dice with 99 sides =' }, |
||||||
|
{ in: '##999d999=10000', out: '##999d999=10000' }, |
||||||
|
{ in: '##0d0', out: '##0d0' }, |
||||||
|
]; |
||||||
|
for(let i in markdownCases) { |
||||||
|
test(`should contain ${markdownCases[i].out} for an input of ${markdownCases[i].in}`, () => { |
||||||
|
const output = markdownCases[i].in.replace(diceroll.regexMarkdown, diceroll.markdown.bind(null, false)); |
||||||
|
expect(output).toContain(markdownCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,19 @@ |
|||||||
|
const escape = require('./escape.js'); |
||||||
|
|
||||||
|
describe('escape() - convert some characters to html entities', () => { |
||||||
|
const cases = [ |
||||||
|
{ in: "'", out: ''' }, |
||||||
|
{ in: '/', out: '/' }, |
||||||
|
{ in: '`', out: '`' }, |
||||||
|
{ in: '=', out: '=' }, |
||||||
|
{ in: '&', out: '&' }, |
||||||
|
{ in: '<', out: '<' }, |
||||||
|
{ in: '>', out: '>' }, |
||||||
|
{ in: '"', out: '"' }, |
||||||
|
]; |
||||||
|
for(let i in cases) { |
||||||
|
test(`should output ${cases[i].out} for an input of ${cases[i].in}`, () => { |
||||||
|
expect(escape(cases[i].in)).toBe(cases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,9 @@ |
|||||||
|
const fortune = require('./fortune.js'); |
||||||
|
|
||||||
|
describe('fortune markdown', () => { |
||||||
|
test(`should contain a random fortune for an input of ##fortune`, () => { |
||||||
|
const output = '##fortune'.replace(fortune.regex, fortune.markdown.bind(null, false)); |
||||||
|
const hasFortuneText = fortune.fortunes.some(f => output.includes(f)); |
||||||
|
expect(hasFortuneText).toBe(true); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,42 @@ |
|||||||
|
const linkmatch = require('./linkmatch.js'); |
||||||
|
const linkRegex = /\[(?<label>[^\[][^\]]*?)\]\((?<url>(?:/[^\s<>\[\]{}|\\^)]+|https?\://[^\s<>\[\]{}|\\^)]+))\)|(?<urlOnly>https?\://[^\s<>\[\]{}|\\^]+)/g; |
||||||
|
const Permission = require('../permission.js') |
||||||
|
const Permissions = require('../permissions.js') |
||||||
|
const ROOT = new Permission(); |
||||||
|
ROOT.setAll(Permission.allPermissions); |
||||||
|
const NO_PERMISSION = new Permission(); |
||||||
|
|
||||||
|
describe('link markdown', () => { |
||||||
|
|
||||||
|
const rootCases = [ |
||||||
|
{ in: 'http://something.com', out: "href=" }, |
||||||
|
{ in: 'https://something.com', out: "href=" }, |
||||||
|
{ in: '[test](http://something.com)', out: ">test</a>" }, |
||||||
|
{ in: '[test](https://something.com)', out: ">test</a>" }, |
||||||
|
{ in: 'http://', out: "http://" }, |
||||||
|
{ in: 'https://', out: "https://" }, |
||||||
|
]; |
||||||
|
|
||||||
|
for(let i in rootCases) { |
||||||
|
test(`should contain ${rootCases[i].out} for an input of ${rootCases[i].in}`, () => { |
||||||
|
expect(rootCases[i].in.replace(linkRegex, linkmatch.bind(null, ROOT))).toContain(rootCases[i].out) |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const cases = [ |
||||||
|
{ in: 'http://something.com', out: "href=" }, |
||||||
|
{ in: 'https://something.com', out: "href=" }, |
||||||
|
{ in: '[http://something.com](test)', out: ">http://something.com</a>" }, |
||||||
|
{ in: '[https://something.com](test)', out: ">https://something.com</a>" }, |
||||||
|
{ in: 'http://', out: "http://" }, |
||||||
|
{ in: 'https://', out: "https://" }, |
||||||
|
]; |
||||||
|
|
||||||
|
for(let i in cases) { |
||||||
|
test(`should contain ${cases[i].out} for an input of ${cases[i].in}`, () => { |
||||||
|
expect(cases[i].in.replace(linkRegex, linkmatch.bind(null, NO_PERMISSION))).toContain(cases[i].out) |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
}); |
@ -0,0 +1,31 @@ |
|||||||
|
const name = require('./name.js'); |
||||||
|
const Permission = require('../permission.js') |
||||||
|
const Permissions = require('../permissions.js') |
||||||
|
const ROOT = new Permission(); |
||||||
|
ROOT.setAll(Permission.allPermissions); |
||||||
|
|
||||||
|
describe('name/trip/capcode handler', () => { |
||||||
|
|
||||||
|
const cases = [ |
||||||
|
{ in: '## Admin', out: { name: 'Anon', tripcode: null, capcode: '## Admin' } }, |
||||||
|
{ in: '## Global Staff', out: { name: 'Anon', tripcode: null, capcode: '## Global Staff' } }, |
||||||
|
{ in: '## Board Owner', out: { name: 'Anon', tripcode: null, capcode: '## Admin' } }, |
||||||
|
{ in: '## Board Mod', out: { name: 'Anon', tripcode: null, capcode: '## Admin' } }, |
||||||
|
{ in: '##', out: { name: 'Anon', tripcode: null, capcode: '## Admin' } }, |
||||||
|
{ in: '', out: { name: 'Anon', tripcode: null, capcode: null } }, |
||||||
|
{ in: 'test', out: { name: 'test', tripcode: null, capcode: null } }, |
||||||
|
{ in: 'test#12345', out: { name: 'test', tripcode: '!CSZ6G0yP9Q', capcode: null } }, |
||||||
|
{ in: '#12345', out: { name: 'Anon', tripcode: '!CSZ6G0yP9Q', capcode: null } }, |
||||||
|
{ in: '#12345## Admin', out: { name: 'Anon', tripcode: '!CSZ6G0yP9Q', capcode: '## Admin' } }, |
||||||
|
{ in: 'test#12345## Admin', out: { name: 'test', tripcode: '!CSZ6G0yP9Q', capcode: '## Admin' } }, |
||||||
|
]; |
||||||
|
|
||||||
|
for(let i in cases) { |
||||||
|
test(`should contain ${cases[i].out.capcode} for an input of ${cases[i].in}`, async () => { |
||||||
|
const output = await name(cases[i].in, ROOT, {forceAnon: false, defaultName: 'Anon'}, 'a', {a:ROOT}, 'b') |
||||||
|
expect(output).toStrictEqual(cases[i].out) |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
}); |
@ -0,0 +1,29 @@ |
|||||||
|
const { getSecureTrip, getInsecureTrip } = require('./tripcode.js'); |
||||||
|
|
||||||
|
describe('getSecureTrip() - "secure" tripcodes', () => { |
||||||
|
const cases = [ |
||||||
|
{ in: '' }, |
||||||
|
{ in: null }, |
||||||
|
{ in: '13245' }, |
||||||
|
{ in: '1324512345123451234512345123451234512345' }, |
||||||
|
]; |
||||||
|
for(let i in cases) { |
||||||
|
test(`should not error for an input of ${cases[i].in}`, async () => { |
||||||
|
expect((await getSecureTrip(cases[i].in))); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
describe('getInsecureTrip() - "insecure" tripcodes', () => { |
||||||
|
const cases = [ |
||||||
|
{ in: '', out: "8NBuQ4l6uQ" }, |
||||||
|
{ in: null, out: "8NBuQ4l6uQ" }, |
||||||
|
{ in: '13245', out: "VPkdFNhOGY" }, |
||||||
|
{ in: '1324512345123451234512345123451234512345', out: "9ovLU2O1wk" }, |
||||||
|
]; |
||||||
|
for(let i in cases) { |
||||||
|
test(`should contain ${cases[i].out} for an input of ${cases[i].in}`, () => { |
||||||
|
expect(getInsecureTrip(cases[i].in)).toBe(cases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,21 @@ |
|||||||
|
const randomRange = require('./randomrange.js'); |
||||||
|
|
||||||
|
describe('randomRange() - return number with secure crypto', () => { |
||||||
|
|
||||||
|
const cases = [ |
||||||
|
{ in: { min: 0, max: 10 } }, |
||||||
|
{ in: { min: 0.5, max: 1 } }, |
||||||
|
{ in: { min: 0, max: 1000 } }, |
||||||
|
{ in: { min: -1, max: 10 } }, |
||||||
|
{ in: { min: 0, max: 0 } }, |
||||||
|
{ in: { min: 1000, max: 1000 } }, |
||||||
|
]; |
||||||
|
for(let i in cases) { |
||||||
|
test(`randomRange should output ${cases[i].in.min}<=out<=${cases[i].in.max} for an input of ${cases[i].in.min}, ${cases[i].in.max}`, async () => { |
||||||
|
const output = await randomRange(cases[i].in.min, cases[i].in.max); |
||||||
|
expect(output).toBeGreaterThanOrEqual(Math.floor(cases[i].in.min)); |
||||||
|
expect(output).toBeLessThanOrEqual(Math.floor(cases[i].in.max)); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,124 @@ |
|||||||
|
const { existsBody, lengthBody, numberBody, numberBodyVariable, |
||||||
|
minmaxBody, inArrayBody, arrayInBody, checkSchema } = require('./schema.js'); |
||||||
|
|
||||||
|
describe('schema checking (input handling after paramconverter)', () => { |
||||||
|
|
||||||
|
const existsBodyCases = [ |
||||||
|
{ in: { body: { test: 1 }, expected: true }, out: 0 }, |
||||||
|
{ in: { body: { test: null }, expected: false }, out: 0 }, |
||||||
|
{ in: { body: {}, expected: false }, out: 0 }, |
||||||
|
{ in: { body: { test: 1 }, expected: false }, out: 1 }, |
||||||
|
{ in: { body: { test: '' }, expected: true }, out: 0 }, |
||||||
|
]; |
||||||
|
for(let i in existsBodyCases) { |
||||||
|
test(`existsBody should output ${existsBodyCases[i].out} for an input of ${existsBodyCases[i].in.body.test}`, async () => { |
||||||
|
const result = await checkSchema([ |
||||||
|
{ result: existsBody(existsBodyCases[i].in.body.test), expected: existsBodyCases[i].in.expected, error: 'error' }, |
||||||
|
]); |
||||||
|
expect(result.length).toBe(existsBodyCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const lengthBodyCases = [ |
||||||
|
{ in: { body: { test: null }, expected: false }, out: 0 }, |
||||||
|
{ in: { body: { test: '' }, expected: false }, out: 0 }, |
||||||
|
{ in: { body: { test: null }, min: 1, expected: false }, out: 1 }, |
||||||
|
{ in: { body: { test: '' }, min: 1, expected: false }, out: 1 }, |
||||||
|
{ in: { body: { test: 'hello' }, expected: false }, out: 0 }, |
||||||
|
{ in: { body: { test: 'hello' }, min: 0, max: 10, expected: false }, out: 0 }, |
||||||
|
{ in: { body: { test: 'hello' }, min: 10, expected: false }, out: 1 }, |
||||||
|
{ in: { body: { test: 'hellohellohello' }, min: 10, expected: false }, out: 0 }, |
||||||
|
{ in: { body: { test: 'hellohellohello' }, min: 0, max: 10, expected: false }, out: 1 }, |
||||||
|
{ in: { body: { test: 'hellohellohello' }, max: 10, expected: false }, out: 1 }, |
||||||
|
]; |
||||||
|
for(let i in lengthBodyCases) { |
||||||
|
test(`lengthBody should output ${lengthBodyCases[i].out} for an input of ${lengthBodyCases[i].in.body.test}`, async () => { |
||||||
|
const result = await checkSchema([ |
||||||
|
{ result: lengthBody(lengthBodyCases[i].in.body.test, lengthBodyCases[i].in.min, lengthBodyCases[i].in.max), expected: lengthBodyCases[i].in.expected, error: 'error' }, |
||||||
|
]); |
||||||
|
expect(result.length).toBe(lengthBodyCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const numberBodyCases = [ |
||||||
|
{ in: { body: { test: null }, expected: false }, out: 0 }, |
||||||
|
{ in: { body: { test: 1 }, expected: true }, out: 0 }, |
||||||
|
{ in: { body: { test: 10 }, max: 10, expected: true }, out: 0 }, |
||||||
|
{ in: { body: { test: 11 }, max: 10, expected: true }, out: 1 }, |
||||||
|
{ in: { body: { test: 9 }, min: 10, expected: true }, out: 1 }, |
||||||
|
{ in: { body: { test: 10 }, min: 10, expected: true }, out: 0 }, |
||||||
|
]; |
||||||
|
for(let i in numberBodyCases) { |
||||||
|
test(`numberBody should output ${numberBodyCases[i].out} for an input of ${numberBodyCases[i].in.body.test}`, async () => { |
||||||
|
const result = await checkSchema([ |
||||||
|
{ result: numberBody(numberBodyCases[i].in.body.test, numberBodyCases[i].in.min, numberBodyCases[i].in.max), expected: numberBodyCases[i].in.expected, error: 'error' }, |
||||||
|
]); |
||||||
|
expect(result.length).toBe(numberBodyCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const numberBodyVariableCases = [ |
||||||
|
{ in: { body: { test: 5 }, minOld: 0, minNew: 0, maxOld: 10, maxNew: 5, expected: true }, out: 0 }, |
||||||
|
{ in: { body: { test: 5 }, minOld: 0, minNew: 6, maxOld: 10, maxNew: 10, expected: true }, out: 1 }, |
||||||
|
]; |
||||||
|
for(let i in numberBodyVariableCases) { |
||||||
|
test(`numberBodyVariable should output ${numberBodyVariableCases[i].out} for an input of ${numberBodyVariableCases[i].in.body.test}`, async () => { |
||||||
|
const { body, minOld, minNew, maxOld, maxNew } = numberBodyVariableCases[i].in; |
||||||
|
const result = await checkSchema([ |
||||||
|
{ result: numberBodyVariable(body.test, minOld, minNew, maxOld, maxNew), expected: numberBodyVariableCases[i].in.expected, error: 'error' }, |
||||||
|
]); |
||||||
|
expect(result.length).toBe(numberBodyVariableCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const minmaxBodyCases = [ |
||||||
|
{ in: { a: 0, b: 100, expected: true }, out: 0 }, |
||||||
|
{ in: { a: 101, b: 100, expected: true }, out: 1 }, |
||||||
|
]; |
||||||
|
for(let i in minmaxBodyCases) { |
||||||
|
test(`minmaxBody should output ${minmaxBodyCases[i].out} for an input of ${minmaxBodyCases[i].in}`, async () => { |
||||||
|
const { a, b, expected } = minmaxBodyCases[i].in; |
||||||
|
const result = await checkSchema([ |
||||||
|
{ result: minmaxBody(a, b), expected, error: 'error' }, |
||||||
|
]); |
||||||
|
expect(result.length).toBe(minmaxBodyCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const inArrayBodyCases = [ |
||||||
|
{ in: { a: null, b: ['a', 'b', 'c'], expected: false }, out: 0 }, |
||||||
|
{ in: { a: 'x', b: [], expected: false }, out: 0 }, |
||||||
|
{ in: { a: 'x', b: ['a', 'b', 'c'], expected: false }, out: 0 }, |
||||||
|
{ in: { a: 'a', b: ['a', 'b', 'c'], expected: true }, out: 0 }, |
||||||
|
{ in: { a: null, b: new Set(['a', 'b', 'c']), expected: false }, out: 0 }, |
||||||
|
{ in: { a: 'x', b: new Set(), expected: false }, out: 0 }, |
||||||
|
{ in: { a: 'x', b: new Set(['a', 'b', 'c']), expected: false }, out: 0 }, |
||||||
|
{ in: { a: 'a', b: new Set(['a', 'b', 'c']), expected: true }, out: 0 }, |
||||||
|
]; |
||||||
|
for(let i in inArrayBodyCases) { |
||||||
|
test(`inArrayBody should output ${inArrayBodyCases[i].out} for an input of ${inArrayBodyCases[i].in}`, async () => { |
||||||
|
const { a, b, expected } = inArrayBodyCases[i].in; |
||||||
|
const result = await checkSchema([ |
||||||
|
{ result: inArrayBody(a, b), expected, error: 'error' }, |
||||||
|
]); |
||||||
|
expect(result.length).toBe(inArrayBodyCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const arrayInBodyCases = [ |
||||||
|
{ in: { a: null, b: ['a', 'b', 'c'], expected: false }, out: 0 }, |
||||||
|
{ in: { a: 'x', b: [], expected: false }, out: 0 }, |
||||||
|
{ in: { a: 'x', b: ['a', 'b', 'c'], expected: false }, out: 0 }, |
||||||
|
{ in: { a: 'a', b: ['a', 'b', 'c'], expected: true }, out: 0 }, |
||||||
|
]; |
||||||
|
for(let i in arrayInBodyCases) { |
||||||
|
test(`arrayInBody should output ${arrayInBodyCases[i].out} for an input of ${arrayInBodyCases[i].in}`, async () => { |
||||||
|
const { a, b, expected } = arrayInBodyCases[i].in; |
||||||
|
const result = await checkSchema([ |
||||||
|
{ result: arrayInBody(b, a), expected, error: 'error' }, |
||||||
|
]); |
||||||
|
expect(result.length).toBe(arrayInBodyCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,69 @@ |
|||||||
|
const { trimSetting, numberSetting, booleanSetting, arraySetting } = require('./setting.js'); |
||||||
|
|
||||||
|
describe('trimSetting, numberSetting, booleanSetting, arraySetting', () => { |
||||||
|
|
||||||
|
const trimCases = [ |
||||||
|
{ in: '', out: '' }, |
||||||
|
{ in: ' lol ', out: 'lol' }, |
||||||
|
{ in: 1, out: 'OLDSETTING' }, |
||||||
|
{ in: null, out: 'OLDSETTING' }, |
||||||
|
]; |
||||||
|
for(let i in trimCases) { |
||||||
|
test(`trimSetting should output ${trimCases[i].out} for an input of ${trimCases[i].in}`, () => { |
||||||
|
expect(trimSetting(trimCases[i].in, 'OLDSETTING')).toStrictEqual(trimCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const numberCases = [ |
||||||
|
{ in: 3, out: 3 }, |
||||||
|
{ in: undefined, out: 'OLDSETTING' }, |
||||||
|
{ in: null, out: 'OLDSETTING' }, |
||||||
|
{ in: [], out: 'OLDSETTING' }, |
||||||
|
{ in: '', out: 'OLDSETTING' }, |
||||||
|
{ in: 'string', out: 'OLDSETTING' }, |
||||||
|
]; |
||||||
|
for(let i in numberCases) { |
||||||
|
test(`numberSetting should output ${numberCases[i].out} for an input of ${numberCases[i].in}`, () => { |
||||||
|
expect(numberSetting(numberCases[i].in, 'OLDSETTING')).toStrictEqual(numberCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const booleanCases = [ |
||||||
|
{ in: null, out: false }, |
||||||
|
{ in: undefined, out: false }, |
||||||
|
{ in: '', out: true }, |
||||||
|
{ in: 'test', out: true }, |
||||||
|
{ in: 1, out: true }, |
||||||
|
{ in: [], out: true }, |
||||||
|
{ in: [1], out: true }, |
||||||
|
]; |
||||||
|
for(let i in booleanCases) { |
||||||
|
test(`booleanSetting should output ${booleanCases[i].out} for an input of ${booleanCases[i].in}`, () => { |
||||||
|
expect(booleanSetting(booleanCases[i].in)).toStrictEqual(booleanCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const arrayCases = [ |
||||||
|
{ in: undefined, out: 'OLDSETTING' }, |
||||||
|
{ in: null, out: 'OLDSETTING' }, |
||||||
|
{ in: '', out: [] }, |
||||||
|
{ in: 'test', out: ['test'] }, |
||||||
|
{ in: 1, out: 'OLDSETTING' }, |
||||||
|
{ in: [], out: 'OLDSETTING' }, |
||||||
|
{ in: '1', out: ['1'] }, |
||||||
|
{ in: `1
|
||||||
|
2 |
||||||
|
3`, out: ['1', '2', '3'] },
|
||||||
|
{ in: ` hello
|
||||||
|
|
||||||
|
123 |
||||||
|
|
||||||
|
xxx`, out: [' hello ', '123', 'xxx'] },
|
||||||
|
]; |
||||||
|
for(let i in arrayCases) { |
||||||
|
test(`arraySetting should output ${arrayCases[i].out} for an input of ${arrayCases[i].in}`, () => { |
||||||
|
expect(arraySetting(arrayCases[i].in, 'OLDSETTING', 10)).toStrictEqual(arrayCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,59 @@ |
|||||||
|
const { getDotProp, includeChildren, compareSettings } = require('./settingsdiff.js'); |
||||||
|
|
||||||
|
describe('getDotProp, includeChildren, compareSettings in settingsdiff', () => { |
||||||
|
|
||||||
|
const getDotPropCases = [ |
||||||
|
{ in: { object: {a:{b:{c:1}}}, prop: 'a.b.c' }, out: 1 }, |
||||||
|
{ in: { object: {a:null}, prop: 'a.b.c' }, out: null }, |
||||||
|
{ in: { object: {}, prop: 'a.b.c' }, out: null }, |
||||||
|
]; |
||||||
|
for(let i in getDotPropCases) { |
||||||
|
test(`getDotProp should output ${getDotPropCases[i].out} for an input of ${getDotPropCases[i].in}`, () => { |
||||||
|
expect(getDotProp(getDotPropCases[i].in.object, getDotPropCases[i].in.prop)).toStrictEqual(getDotPropCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const includeChildrenCases = [ |
||||||
|
{ in: { object: {a:{b:1,c:2,d:3},a2:{b:'notme'}}, prop: 'a' }, out: {'a.b':['example'], 'a.c':['example'], 'a.d':['example']} }, |
||||||
|
{ in: { object: null, prop: 'a' }, out: {} }, |
||||||
|
{ in: { object: {a:null}, prop: 'a' }, out: {} }, |
||||||
|
]; |
||||||
|
for(let i in includeChildrenCases) { |
||||||
|
test(`includeChildren should output ${includeChildrenCases[i].out} for an input of ${includeChildrenCases[i].in}`, () => { |
||||||
|
expect(includeChildren(includeChildrenCases[i].in.object, includeChildrenCases[i].in.prop, ['example'])).toStrictEqual(includeChildrenCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const compareSettingsCases = [ |
||||||
|
{ |
||||||
|
in: { |
||||||
|
entries: [['a.b.c',['1']]], |
||||||
|
old: {a:{b:{c:1,d:2}}}, |
||||||
|
new: {a:{b:{c:1,d:2}}}, |
||||||
|
}, |
||||||
|
out: new Set(), |
||||||
|
}, |
||||||
|
{ |
||||||
|
in: { |
||||||
|
entries: [['a.b.c',['1']]], |
||||||
|
old: {a:{b:{c:1,d:2}}}, |
||||||
|
new: {a:{b:{c:1,d:3}}}, |
||||||
|
}, |
||||||
|
out: new Set(), |
||||||
|
}, |
||||||
|
{ |
||||||
|
in: { |
||||||
|
entries: [['a.b.c',['1']]], |
||||||
|
old: {a:{b:{c:1,d:2}}}, |
||||||
|
new: {a:{b:{c:2,d:2}}}, |
||||||
|
}, |
||||||
|
out: new Set(['1']), |
||||||
|
}, |
||||||
|
]; |
||||||
|
for(let i in compareSettingsCases) { |
||||||
|
test(`compareSettings should output ${compareSettingsCases[i].out} for an input of ${compareSettingsCases[i].in}`, () => { |
||||||
|
expect(compareSettings(compareSettingsCases[i].in.entries, compareSettingsCases[i].in.old, compareSettingsCases[i].in.new, 4)).toStrictEqual(compareSettingsCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,56 @@ |
|||||||
|
const { relativeString, relativeColor, durationString } = require('./timeutils.js'); |
||||||
|
|
||||||
|
describe('timeutils relativeString, relativeColor, durationString', () => { |
||||||
|
|
||||||
|
const relativeStringCases = [ |
||||||
|
{ in: { start: new Date('2022-04-07T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: 'Now'}, |
||||||
|
{ in: { start: new Date('2022-04-07T08:01:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '1 minute ago'}, |
||||||
|
{ in: { start: new Date('2022-04-07T08:01:29.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '1 minute ago'}, |
||||||
|
{ in: { start: new Date('2022-04-07T08:01:30.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '2 minutes ago'}, |
||||||
|
{ in: { start: new Date('2022-04-07T08:00:00.000Z'), end: new Date('2022-04-07T08:02:00.000Z') }, out: '2 minutes from now'}, |
||||||
|
{ in: { start: new Date('2022-04-07T11:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '3 hours ago'}, |
||||||
|
{ in: { start: new Date('2022-04-10T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '3 days ago'}, |
||||||
|
{ in: { start: new Date('2022-04-28T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '3 weeks ago'}, |
||||||
|
{ in: { start: new Date('2022-07-07T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '3 months ago'}, |
||||||
|
{ in: { start: new Date('2022-07-08T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '3 months ago'}, |
||||||
|
{ in: { start: new Date('2022-07-25T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '4 months ago'}, |
||||||
|
{ in: { start: new Date('2023-04-07T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '1 year ago'}, |
||||||
|
{ in: { start: new Date('2032-04-07T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '10 years ago'}, |
||||||
|
{ in: { start: new Date('2132-04-07T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '110 years ago'}, |
||||||
|
{ in: { end: new Date('2132-04-07T08:00:00.000Z'), start: new Date('2022-04-07T08:00:00.000Z') }, out: '110 years from now'}, |
||||||
|
]; |
||||||
|
for(let i in relativeStringCases) { |
||||||
|
test(`relativeString should output ${relativeStringCases[i].out} for an input of ${relativeStringCases[i].in}`, () => { |
||||||
|
expect(relativeString(relativeStringCases[i].in.start, relativeStringCases[i].in.end)).toStrictEqual(relativeStringCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const relativeColorCases = [ |
||||||
|
{ in: { start: new Date('2022-04-07T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '#0098ff'}, |
||||||
|
{ in: { start: new Date('2022-04-10T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '#d9aa00'}, |
||||||
|
{ in: { start: new Date('2023-04-07T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '#000000'}, |
||||||
|
{ in: { start: new Date('2032-04-07T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '#000000'}, |
||||||
|
{ in: { start: new Date('2132-04-07T08:00:00.000Z'), end: new Date('2022-04-07T08:00:00.000Z') }, out: '#000000'}, |
||||||
|
{ in: { end: new Date('2132-04-07T08:00:00.000Z'), start: new Date('2022-04-07T08:00:00.000Z') }, out: '#0098ff'}, |
||||||
|
]; |
||||||
|
for(let i in relativeColorCases) { |
||||||
|
test(`relativeColor should output ${relativeColorCases[i].out} for an input of ${relativeColorCases[i].in}`, () => { |
||||||
|
expect(relativeColor(relativeColorCases[i].in.start, relativeColorCases[i].in.end)).toStrictEqual(relativeColorCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const durationStringCases = [ |
||||||
|
{ in: 0*1000, out: '00:00' }, |
||||||
|
{ in: 10*1000, out: '00:10' }, |
||||||
|
{ in: 100*1000, out: '01:40' }, |
||||||
|
{ in: 121*1000, out: '02:01' }, |
||||||
|
{ in: 2*60*60*1000, out: '02:00:00' }, |
||||||
|
{ in: 999*60*60*1000, out: '999:00:00' }, |
||||||
|
]; |
||||||
|
for(let i in durationStringCases) { |
||||||
|
test(`durationString should output ${durationStringCases[i].out} for an input of ${durationStringCases[i].in}`, () => { |
||||||
|
expect(durationString(durationStringCases[i].in)).toStrictEqual(durationStringCases[i].out); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,658 @@ |
|||||||
|
const fetch = require('node-fetch'); |
||||||
|
const FormData = require('form-data'); |
||||||
|
const fs = require('fs-extra'); |
||||||
|
|
||||||
|
module.exports = () => describe('Test post modactions', () => { |
||||||
|
|
||||||
|
let sessionCookie |
||||||
|
, csrfToken; |
||||||
|
|
||||||
|
test('login as admin', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('username', 'admin'); |
||||||
|
params.append('password', process.env.TEST_ADMIN_PASSWORD); |
||||||
|
const response = await fetch('http://localhost/forms/login', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
const rawHeaders = response.headers.raw(); |
||||||
|
expect(rawHeaders['set-cookie']).toBeDefined(); |
||||||
|
expect(rawHeaders['set-cookie'][0]).toMatch(/^connect\.sid/); |
||||||
|
sessionCookie = rawHeaders['set-cookie'][0]; |
||||||
|
csrfToken = await fetch('http://localhost/csrf.json', { headers: { 'cookie': sessionCookie }}) |
||||||
|
.then(res => res.json()) |
||||||
|
.then(json => json.token); |
||||||
|
}); |
||||||
|
|
||||||
|
jest.setTimeout(5*60*1000); //give a generous timeout
|
||||||
|
test('make new 10 threads with 10 replies each', async () => { |
||||||
|
const threadParams = new URLSearchParams(); |
||||||
|
threadParams.append('message', Math.random()); |
||||||
|
threadParams.append('captcha', '000000'); |
||||||
|
const promises = []; |
||||||
|
for (let t = 0; t < 10; t++) { |
||||||
|
const promise = fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: threadParams |
||||||
|
}).then(async (response) => { |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const thread = (await response.json()).postId; |
||||||
|
for (let r = 0; r < 10; r++) { |
||||||
|
const replyParams = new URLSearchParams(); |
||||||
|
replyParams.append('message', Math.random()); |
||||||
|
replyParams.append('thread', thread); |
||||||
|
replyParams.append('captcha', '000000'); |
||||||
|
const promise2 = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: replyParams |
||||||
|
}).then(async (response2) => { |
||||||
|
expect(response2.ok).toBe(true); |
||||||
|
}); |
||||||
|
promises.push(promise2); |
||||||
|
} |
||||||
|
}); |
||||||
|
promises.push(promise); |
||||||
|
} |
||||||
|
await Promise.all(promises); //wait for all posts to go through
|
||||||
|
jest.setTimeout(5*1000); //back to normal timeout
|
||||||
|
await new Promise((resolve) => { setTimeout(resolve, 1000) }); //wait for async builds
|
||||||
|
}); |
||||||
|
|
||||||
|
test('bumplock, lock, and sticky 5 random posts from /test/', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
sticky: '1', |
||||||
|
bumplock: '1', |
||||||
|
lock: '1', |
||||||
|
}); |
||||||
|
for (let i = 0; i < 5; i++) { |
||||||
|
const thread = threads[Math.floor(Math.random() * threads.length)]; |
||||||
|
params.append('checkedposts', thread.postId); |
||||||
|
} |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
await new Promise((resolve) => { setTimeout(resolve, 1000) }); //wait for async builds
|
||||||
|
}); |
||||||
|
|
||||||
|
test('remove the bumplock, lock and sticky on any threads', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
sticky: '0', |
||||||
|
bumplock: '1', //these are a "toggle",
|
||||||
|
lock: '1', |
||||||
|
}); |
||||||
|
threads.filter(t => t.locked).forEach(t => params.append('checkedposts', t.postId)); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete 5 random posts from /test/', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
delete: '1', |
||||||
|
}); |
||||||
|
for (let i = 0; i < 5; i++) { |
||||||
|
const thread = threads[Math.floor(Math.random() * threads.length)]; |
||||||
|
params.append('checkedposts', thread.postId); |
||||||
|
} |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('lower reply limit', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
name: 'test', |
||||||
|
description: '', |
||||||
|
tags: '', |
||||||
|
announcement: '', |
||||||
|
theme: 'yotsuba-b', |
||||||
|
code_theme: 'ir-black', |
||||||
|
custom_css: '', |
||||||
|
enable_tegaki: 'true', |
||||||
|
max_files: '5', |
||||||
|
files_allow_video: 'true', |
||||||
|
files_allow_image: 'true', |
||||||
|
files_allow_animated_image: 'true', |
||||||
|
files_allow_audio: 'true', |
||||||
|
user_post_spoiler: 'true', |
||||||
|
user_post_unlink: 'true', |
||||||
|
default_name: 'Anon', |
||||||
|
user_post_delete: 'true', |
||||||
|
min_thread_message_length: '0', |
||||||
|
min_reply_message_length: '0', |
||||||
|
max_thread_message_length: '20000', |
||||||
|
max_reply_message_length: '20000', |
||||||
|
thread_limit: '100', |
||||||
|
reply_limit: '20', |
||||||
|
bump_limit: '500', |
||||||
|
file_r9k_mode: '0', |
||||||
|
message_r9k_mode: '0', |
||||||
|
delete_protection_count: '0', |
||||||
|
delete_protection_age: '0', |
||||||
|
lock_mode: '0', |
||||||
|
captcha_mode: '2', |
||||||
|
pph_trigger: '50', |
||||||
|
pph_trigger_action: '2', |
||||||
|
tph_trigger: '10', |
||||||
|
tph_trigger_action: '1', |
||||||
|
lock_reset: '0', |
||||||
|
captcha_reset: '0', |
||||||
|
filters: '', |
||||||
|
filter_mode: '0', |
||||||
|
ban_duration: '0' |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/settings', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
expect(response.status).toBe(200); |
||||||
|
}); |
||||||
|
|
||||||
|
jest.setTimeout(5*60*1000); //give generous timeout
|
||||||
|
test('make new cyclic thread and check it prunes replies after the limit', async () => { |
||||||
|
|
||||||
|
//new thread
|
||||||
|
const threadParams = new URLSearchParams(); |
||||||
|
threadParams.append('message', Math.random()); |
||||||
|
threadParams.append('captcha', '000000'); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: threadParams |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const thread = (await response.json()).postId; |
||||||
|
|
||||||
|
//make it cyclic
|
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
cyclic: '1', |
||||||
|
checkedposts: thread, |
||||||
|
}); |
||||||
|
const response2 = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response2.ok).toBe(true); |
||||||
|
|
||||||
|
//make the replies
|
||||||
|
const promises = []; |
||||||
|
for (let r = 0; r < 25; r++) { |
||||||
|
const replyParams = new URLSearchParams(); |
||||||
|
replyParams.append('message', Math.random()); |
||||||
|
replyParams.append('thread', thread); |
||||||
|
replyParams.append('captcha', '000000'); |
||||||
|
const promise = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: replyParams |
||||||
|
}).then(response3 => { |
||||||
|
expect(response3.ok).toBe(true); |
||||||
|
}); |
||||||
|
promises.push(promise); |
||||||
|
} |
||||||
|
await Promise.all(promises); //wait for all posts to go through
|
||||||
|
|
||||||
|
//check the replies in json
|
||||||
|
const response3 = await fetch(`http://localhost/test/thread/${thread}.json`).then(res => res.json()); |
||||||
|
expect(response3.replies.length).toBe(20); |
||||||
|
jest.setTimeout(5*1000); //back to normal timeout
|
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('move/merge a thread', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedposts: threads[Math.floor(threads.length/2)].postId, |
||||||
|
move: '1', |
||||||
|
move_to_thread: threads[0].postId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
let reportedPost; |
||||||
|
test('test local report + global report', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const randomThread = threads[Math.floor(Math.random() * threads.length)]; |
||||||
|
reportedPost = randomThread; |
||||||
|
const randomThreadId = randomThread.postId; |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
report: '1', |
||||||
|
global_report: '1', |
||||||
|
report_reason: 'test', |
||||||
|
checkedposts: randomThreadId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('test post editing, add a bunch of markdown', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const randomThreadId = threads[Math.floor(Math.random() * threads.length)].postId; |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
board: 'test', |
||||||
|
postId: randomThreadId, |
||||||
|
subject: 'NEW SUBJECT', |
||||||
|
email: 'NEW EMAIL', |
||||||
|
name: 'NEW NAME', |
||||||
|
message: `>greentext
|
||||||
|
<pinktext |
||||||
|
==title== |
||||||
|
''bold'' |
||||||
|
__underline__ |
||||||
|
~strikethrough~~ |
||||||
|
||spoiler text|| |
||||||
|
**italic** |
||||||
|
(((detected))) |
||||||
|
##2d9+3 |
||||||
|
https://example.com
|
||||||
|
[Board Rules](https://your.imageboard/a/custompage/rules.html)(staff only)
|
||||||
|
>>123 |
||||||
|
>>>/test/ |
||||||
|
>>>/test/123 |
||||||
|
\`inline monospace\` |
||||||
|
[code]language |
||||||
|
int main() {...} |
||||||
|
[/code] |
||||||
|
|
||||||
|
[code]aa |
||||||
|
∧_∧ |
||||||
|
( ・ω・) Let's try that again. |
||||||
|
[/code]`, |
||||||
|
log_message: 'test edit', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/editpost', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
let postId; |
||||||
|
test('make post with image', async () => { |
||||||
|
const threadParams = new FormData({ |
||||||
|
message: Math.random(), |
||||||
|
captcha: '000000', |
||||||
|
}); |
||||||
|
const filePath = 'gulp/res/img/flags.png'; |
||||||
|
const fileSizeInBytes = fs.statSync(filePath).size; |
||||||
|
const fileStream = fs.createReadStream(filePath); |
||||||
|
threadParams.append('file', fileStream, { knownLength: fileSizeInBytes }); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
...threadParams.getHeaders(), |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: threadParams |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
postId = (await response.json()).postId; |
||||||
|
}); |
||||||
|
|
||||||
|
test('spoiler the file in a post', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
spoiler: '1', |
||||||
|
checkedposts: postId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('unlink file', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
unlink_file: '1', |
||||||
|
checkedposts: postId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('make post with already spoilered image', async () => { |
||||||
|
const threadParams = new FormData({ |
||||||
|
message: Math.random(), |
||||||
|
captcha: '000000', |
||||||
|
spoiler_all: '1', |
||||||
|
}); |
||||||
|
const filePath = 'gulp/res/img/flags.png'; |
||||||
|
const fileSizeInBytes = fs.statSync(filePath).size; |
||||||
|
const fileStream = fs.createReadStream(filePath); |
||||||
|
threadParams.append('file', fileStream, { knownLength: fileSizeInBytes }); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
...threadParams.getHeaders(), |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: threadParams |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
postId = (await response.json()).postId; |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete file', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
delete_file: '1', |
||||||
|
checkedposts: postId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('ban reporter for local report', async () => { |
||||||
|
const reportsPage = await fetch('http://dev-jschan/test/manage/reports.html', { |
||||||
|
headers: { |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
}).then(res => res.text()); |
||||||
|
const checkString = 'name="checkedreports" value="'; |
||||||
|
const checkIndex = reportsPage.indexOf(checkString); |
||||||
|
const reportId = reportsPage.substring(checkIndex+checkString.length, checkIndex+checkString.length+24); |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
report_ban: '1', |
||||||
|
checkedposts: reportedPost.postId, |
||||||
|
checkedreports: reportId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('dismiss local report', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
dismiss: '1', |
||||||
|
checkedposts: reportedPost.postId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('ban reporter for global report', async () => { |
||||||
|
const reportsPage = await fetch('http://dev-jschan/globalmanage/reports.html', { |
||||||
|
headers: { |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
}).then(res => res.text()); |
||||||
|
const checkString = 'name="checkedreports" value="'; |
||||||
|
const checkIndex = reportsPage.indexOf(checkString); |
||||||
|
const reportId = reportsPage.substring(checkIndex+checkString.length, checkIndex+checkString.length+24); |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
global_report_ban: '1', |
||||||
|
globalcheckedposts: reportedPost._id, |
||||||
|
checkedreports: reportId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/actions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('remove global report ban', async () => { |
||||||
|
const banPage = await fetch('http://localhost/globalmanage/bans.html', { |
||||||
|
headers: { |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
}).then(res => res.text()); |
||||||
|
const checkString = 'name="checkedbans" value="'; |
||||||
|
const checkIndex = banPage.indexOf(checkString); |
||||||
|
banId = banPage.substring(checkIndex+checkString.length, checkIndex+checkString.length+24); |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedbans: banId, |
||||||
|
option: 'unban', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/editbans', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('test banning', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const randomThreadId = threads[Math.floor(Math.random() * threads.length)].postId; |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
ban: '1', |
||||||
|
checkedposts: randomThreadId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('test global ban', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const randomThreadId = threads[Math.floor(Math.random() * threads.length)].postId; |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
global_ban: '1', |
||||||
|
checkedposts: randomThreadId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
let banId |
||||||
|
test('deny ban appeal', async () => { |
||||||
|
const banPage = await fetch('http://localhost/globalmanage/bans.html', { |
||||||
|
headers: { |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
}).then(res => res.text()); |
||||||
|
const checkString = 'name="checkedbans" value="'; |
||||||
|
const checkIndex = banPage.indexOf(checkString); |
||||||
|
banId = banPage.substring(checkIndex+checkString.length, checkIndex+checkString.length+24); |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedbans: banId, |
||||||
|
option: 'deny_appeal', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/editbans', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('edit ban duration', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedbans: banId, |
||||||
|
option: 'edit', |
||||||
|
ban_duration: '3d', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/editbans', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('remove ban', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedbans: banId, |
||||||
|
option: 'unban', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/editbans', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('test ban + qrange + non-appealable + show post in ban + hide name in modlog + modlog message + ban reason', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const randomThreadId = threads[Math.floor(Math.random() * threads.length)].postId; |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
ban: '1', |
||||||
|
ban_q: '1', |
||||||
|
ban_reason: 'test', |
||||||
|
log_message: 'test', |
||||||
|
preserve_post: '1', |
||||||
|
hide_name: '1', |
||||||
|
no_appeal: '1', |
||||||
|
checkedposts: randomThreadId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,286 @@ |
|||||||
|
const fetch = require('node-fetch'); |
||||||
|
const FormData = require('form-data'); |
||||||
|
const fs = require('fs-extra'); |
||||||
|
|
||||||
|
module.exports = () => describe('test some global form submissions', () => { |
||||||
|
|
||||||
|
let sessionCookie |
||||||
|
, csrfToken; |
||||||
|
|
||||||
|
test('login as admin', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('username', 'admin'); |
||||||
|
params.append('password', process.env.TEST_ADMIN_PASSWORD); |
||||||
|
const response = await fetch('http://localhost/forms/login', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
const rawHeaders = response.headers.raw(); |
||||||
|
expect(rawHeaders['set-cookie']).toBeDefined(); |
||||||
|
expect(rawHeaders['set-cookie'][0]).toMatch(/^connect\.sid/); |
||||||
|
sessionCookie = rawHeaders['set-cookie'][0]; |
||||||
|
csrfToken = await fetch('http://localhost/csrf.json', { headers: { 'cookie': sessionCookie }}) |
||||||
|
.then(res => res.json()) |
||||||
|
.then(json => json.token); |
||||||
|
}); |
||||||
|
|
||||||
|
let bannerId, assetId; |
||||||
|
test('add banner', async () => { |
||||||
|
const fileParams = new FormData(); |
||||||
|
const filePath = 'gulp/res/img/flags.png'; |
||||||
|
const fileSizeInBytes = fs.statSync(filePath).size; |
||||||
|
const fileStream = fs.createReadStream(filePath); |
||||||
|
fileParams.append('_csrf', csrfToken); |
||||||
|
fileParams.append('file', fileStream, { knownLength: fileSizeInBytes }); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/addbanners', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
...fileParams.getHeaders(), |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: fileParams |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const bannerPage = await fetch('http://localhost/test/manage/assets.html', { |
||||||
|
headers: { |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
}).then(res => res.text()); |
||||||
|
const checkString = 'name="checkedbanners" value="'; |
||||||
|
const checkIndex = bannerPage.indexOf(checkString); |
||||||
|
const bannerSubstring = bannerPage.substring(checkIndex+checkString.length, checkIndex+checkString.length+70); |
||||||
|
bannerId = bannerSubstring.substring(0, bannerSubstring.lastIndexOf('"')); |
||||||
|
}); |
||||||
|
|
||||||
|
test('add flag', async () => { |
||||||
|
const fileParams = new FormData(); |
||||||
|
const filePath = 'gulp/res/img/flags.png'; |
||||||
|
const fileSizeInBytes = fs.statSync(filePath).size; |
||||||
|
const fileStream = fs.createReadStream(filePath); |
||||||
|
fileParams.append('_csrf', csrfToken); |
||||||
|
fileParams.append('file', fileStream, { knownLength: fileSizeInBytes }); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/addflags', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
...fileParams.getHeaders(), |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: fileParams |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
flagId = 'flags'; |
||||||
|
}); |
||||||
|
|
||||||
|
test('add asset', async () => { |
||||||
|
const fileParams = new FormData(); |
||||||
|
const filePath = 'gulp/res/img/flags.png'; |
||||||
|
const fileSizeInBytes = fs.statSync(filePath).size; |
||||||
|
const fileStream = fs.createReadStream(filePath); |
||||||
|
fileParams.append('_csrf', csrfToken); |
||||||
|
fileParams.append('file', fileStream, { knownLength: fileSizeInBytes }); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/addassets', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
...fileParams.getHeaders(), |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: fileParams |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const assetPage = await fetch('http://localhost/test/manage/assets.html', { |
||||||
|
headers: { |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
}).then(res => res.text()); |
||||||
|
const checkString = 'name="checkedassets" value="'; |
||||||
|
const checkIndex = assetPage.indexOf(checkString); |
||||||
|
const assetSubstring = assetPage.substring(checkIndex+checkString.length, checkIndex+checkString.length+70); |
||||||
|
assetId = assetSubstring.substring(0, assetSubstring.lastIndexOf('"')); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete banner', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedbanners: bannerId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/deletebanners', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete flag', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedflags: flagId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/deleteflags', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete asset', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedassets: assetId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/deleteassets', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('add custompage', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
page: 'test', |
||||||
|
title: 'test', |
||||||
|
message: `==This is a test custompage==
|
||||||
|
wow |
||||||
|
>very cool |
||||||
|
testing 123` |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/addcustompages', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const response2 = await fetch('http://localhost/test/custompage/test.html'); |
||||||
|
expect(response2.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('edit and rename/move custompage', async () => { |
||||||
|
const custompagesPage = await fetch('http://localhost/test/manage/custompages.html', { |
||||||
|
headers: { |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
}).then(res => res.text()); |
||||||
|
const custompageId = custompagesPage.match(/href="\/test\/manage\/editcustompage\/([0-9a-f]{24}).html"/)[1]; |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
page_id: custompageId, |
||||||
|
page: 'test2', |
||||||
|
title: 'test2', |
||||||
|
message: 'test2', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/editcustompage', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const response2 = await fetch('http://localhost/test/custompage/test.html'); |
||||||
|
expect(response2.status).toBe(404); |
||||||
|
const response3 = await fetch('http://localhost/test/custompage/test2.html'); |
||||||
|
expect(response3.status).toBe(200); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete custompage', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedcustompages: 'test2', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/deletecustompages', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const response2 = await fetch('http://localhost/test/custompage/test2.html'); |
||||||
|
expect(response2.status).toBe(404); |
||||||
|
}); |
||||||
|
|
||||||
|
test('add staff', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
username: 'test', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/addstaff', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('edit staff permission', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
username: 'test', |
||||||
|
MANAGE_BOARD_BANS: '1', |
||||||
|
MANAGE_BOARD_LOGS: '1', |
||||||
|
MANAGE_BOARD_SETTINGS: '1', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/editstaff', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('remove staff', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedstaff: 'test', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/deletestaff', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,109 @@ |
|||||||
|
const fetch = require('node-fetch'); |
||||||
|
|
||||||
|
module.exports = () => describe('delete tests and cleanup', () => { |
||||||
|
|
||||||
|
let sessionCookie |
||||||
|
, csrfToken; |
||||||
|
|
||||||
|
test('login as admin', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('username', 'admin'); |
||||||
|
params.append('password', process.env.TEST_ADMIN_PASSWORD); |
||||||
|
const response = await fetch('http://localhost/forms/login', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
const rawHeaders = response.headers.raw(); |
||||||
|
expect(rawHeaders['set-cookie']).toBeDefined(); |
||||||
|
expect(rawHeaders['set-cookie'][0]).toMatch(/^connect\.sid/); |
||||||
|
sessionCookie = rawHeaders['set-cookie'][0]; |
||||||
|
csrfToken = await fetch('http://localhost/csrf.json', { headers: { 'cookie': sessionCookie }}) |
||||||
|
.then(res => res.json()) |
||||||
|
.then(json => json.token); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete_ip_thread test', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
//delete a reply and check if the OP is deleted (ip is the same for all posts atm)
|
||||||
|
const randomThreadId = threads.find(t => t.replyposts > 0).postId; |
||||||
|
const thread = await fetch(`http://localhost/test/thread/${randomThreadId}.json`).then(res => res.json()); |
||||||
|
const post = thread.replies[Math.floor(Math.random() * thread.replies.length)]; |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
delete_ip_thread: '1', |
||||||
|
checkedposts: post.postId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const response2 = await fetch(`http://localhost/test/thread/${randomThreadId}.json`); |
||||||
|
expect(response2.status).toBe(404); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete_ip_board test', async () => { |
||||||
|
const threads = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
const randomThreadId = threads[Math.floor(Math.random() * threads.length)].postId; |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
delete_ip_board: '1', |
||||||
|
checkedposts: randomThreadId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/modactions', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
await new Promise((resolve) => { setTimeout(resolve, 1000) }); //wait for async builds
|
||||||
|
const response2 = await fetch('http://localhost/test/catalog.json').then(res => res.json()); |
||||||
|
expect(response2.length).toBe(0); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete test board', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('_csrf', csrfToken); |
||||||
|
params.append('uri', 'test'); |
||||||
|
params.append('confirm', 'true'); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/deleteboard', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
expect([200, 404]).toContain(response.status) |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete test account', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkedaccounts: 'test', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/deleteaccounts', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,139 @@ |
|||||||
|
const fetch = require('node-fetch'); |
||||||
|
|
||||||
|
module.exports = () => describe('test some global form submissions', () => { |
||||||
|
|
||||||
|
let sessionCookie |
||||||
|
, csrfToken; |
||||||
|
|
||||||
|
test('login as admin', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('username', 'admin'); |
||||||
|
params.append('password', process.env.TEST_ADMIN_PASSWORD); |
||||||
|
const response = await fetch('http://localhost/forms/login', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
const rawHeaders = response.headers.raw(); |
||||||
|
expect(rawHeaders['set-cookie']).toBeDefined(); |
||||||
|
expect(rawHeaders['set-cookie'][0]).toMatch(/^connect\.sid/); |
||||||
|
sessionCookie = rawHeaders['set-cookie'][0]; |
||||||
|
csrfToken = await fetch('http://localhost/csrf.json', { headers: { 'cookie': sessionCookie }}) |
||||||
|
.then(res => res.json()) |
||||||
|
.then(json => json.token); |
||||||
|
}); |
||||||
|
|
||||||
|
let newsId; |
||||||
|
test('add news post', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
title: 'Newspost title~', |
||||||
|
message: `==This is news==
|
||||||
|
wow |
||||||
|
>very cool |
||||||
|
testing 123` |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/addnews', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const newsPage = await fetch('http://localhost/globalmanage/news.html', { |
||||||
|
headers: { |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
}).then(res => res.text()); |
||||||
|
const checkIndex = newsPage.indexOf('name="checkednews" value="'); |
||||||
|
newsId = newsPage.substring(checkIndex+26, checkIndex+26+24); |
||||||
|
}); |
||||||
|
|
||||||
|
test('edit news post', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
news_id: newsId, |
||||||
|
title: 'edited title', |
||||||
|
message: 'edited message', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/editnews', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const newsPage = await fetch('http://localhost/globalmanage/news.html', { |
||||||
|
headers: { |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
}).then(res => res.text()); |
||||||
|
const editTextIndex = newsPage.indexOf('edited title'); |
||||||
|
expect(editTextIndex).not.toBe(-1); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete news post', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
checkednews: newsId, |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/deletenews', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('register test account', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
username: 'test', |
||||||
|
password: 'test', |
||||||
|
passwordconfirm: 'test', |
||||||
|
captcha: '000000', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/register', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.status).toBe(302); |
||||||
|
}); |
||||||
|
|
||||||
|
test('edit account permission', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
username: 'test', |
||||||
|
template: 'fz/P4B//gAA=', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/editaccount', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}) |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,9 @@ |
|||||||
|
describe('run integration tests', () => { |
||||||
|
require('./setup.js')(); |
||||||
|
require('./posting.js')(); |
||||||
|
require('./global.js')(); |
||||||
|
require('./actions.js')(); |
||||||
|
require('./board.js')(); |
||||||
|
require('./pages.js')(); |
||||||
|
require('./cleanup.js')(); |
||||||
|
}) |
@ -0,0 +1,59 @@ |
|||||||
|
const fetch = require('node-fetch'); |
||||||
|
|
||||||
|
module.exports = () => describe('Test loading a bunch of pages', () => { |
||||||
|
|
||||||
|
const urls = [ |
||||||
|
'boards.html', |
||||||
|
'boards.json', |
||||||
|
'boards.json?search=test&sort=popularity&direction=desc', |
||||||
|
'boards.html?search=test&sort=popularity&direction=desc', |
||||||
|
'overboard.html', |
||||||
|
'overboard.json', |
||||||
|
'overboard.html?add=test&rem=abc', |
||||||
|
'overboard.json?add=test&rem=abc', |
||||||
|
'index.html', |
||||||
|
'news.html', |
||||||
|
'rules.html', |
||||||
|
'faq.html', |
||||||
|
'globalmanage/recent.html', |
||||||
|
'globalmanage/reports.html', |
||||||
|
'globalmanage/bans.html', |
||||||
|
'globalmanage/boards.html', |
||||||
|
'globalmanage/globallogs.html', |
||||||
|
'globalmanage/accounts.html', |
||||||
|
'globalmanage/roles.html', |
||||||
|
'globalmanage/news.html', |
||||||
|
'globalmanage/settings.html', |
||||||
|
] |
||||||
|
|
||||||
|
let sessionCookie |
||||||
|
test('login as admin', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('username', 'admin'); |
||||||
|
params.append('password', process.env.TEST_ADMIN_PASSWORD); |
||||||
|
const response = await fetch('http://localhost/forms/login', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
const rawHeaders = response.headers.raw(); |
||||||
|
expect(rawHeaders['set-cookie']).toBeDefined(); |
||||||
|
expect(rawHeaders['set-cookie'][0]).toMatch(/^connect\.sid/); |
||||||
|
sessionCookie = rawHeaders['set-cookie'][0]; |
||||||
|
}); |
||||||
|
|
||||||
|
urls.forEach(u => { |
||||||
|
test(u, async () => { |
||||||
|
const response = await fetch(`http://localhost/${u}`, { |
||||||
|
headers: { |
||||||
|
cookie: sessionCookie, |
||||||
|
}, |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
}) |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,132 @@ |
|||||||
|
const fetch = require('node-fetch'); |
||||||
|
|
||||||
|
module.exports = () => describe('Test posting', () => { |
||||||
|
|
||||||
|
let threadId; |
||||||
|
test('post new thread', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('message', Math.random()); |
||||||
|
params.append('captcha', '000000'); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
threadId = (await response.json()).postId; |
||||||
|
}); |
||||||
|
|
||||||
|
let replyId; |
||||||
|
test('post reply', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('message', Math.random()); |
||||||
|
params.append('thread', threadId); |
||||||
|
params.append('captcha', '000000'); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
replyId = (await response.json()).postId; |
||||||
|
}); |
||||||
|
|
||||||
|
test('post reply with quotes', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('message', `>>${threadId} |
||||||
|
>>${replyId}`);
|
||||||
|
params.append('thread', threadId); |
||||||
|
params.append('captcha', '000000'); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
test('post reply with all markdowns', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('captcha', '000000'); |
||||||
|
params.append('message', `>greentext
|
||||||
|
<pinktext |
||||||
|
==title== |
||||||
|
''bold'' |
||||||
|
__underline__ |
||||||
|
~strikethrough~~ |
||||||
|
||spoiler text|| |
||||||
|
**italic** |
||||||
|
(((detected))) |
||||||
|
##2d9+3 |
||||||
|
https://example.com
|
||||||
|
[Board Rules](https://your.imageboard/a/custompage/rules.html)(staff only)
|
||||||
|
>>${threadId} |
||||||
|
>>>/test/ |
||||||
|
>>>/test/${threadId} |
||||||
|
\`inline monospace\` |
||||||
|
[code]language |
||||||
|
int main() {...} |
||||||
|
[/code] |
||||||
|
|
||||||
|
[code]aa |
||||||
|
∧_∧ |
||||||
|
( ・ω・) Let's try that again. |
||||||
|
[/code] |
||||||
|
`);
|
||||||
|
params.append('thread', threadId); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params |
||||||
|
}); |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
}); |
||||||
|
|
||||||
|
jest.setTimeout(5*60*1000); //give a generous timeout
|
||||||
|
test('post 100 threads with 10 replies each', async () => { |
||||||
|
const threadParams = new URLSearchParams(); |
||||||
|
threadParams.append('message', Math.random()); |
||||||
|
threadParams.append('captcha', '000000'); |
||||||
|
const promises = []; |
||||||
|
for (let t = 0; t < 100; t++) { |
||||||
|
const promise = fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: threadParams |
||||||
|
}).then(async (response) => { |
||||||
|
expect(response.ok).toBe(true); |
||||||
|
const thread = (await response.json()).postId; |
||||||
|
for (let r = 0; r < 10; r++) { |
||||||
|
const replyParams = new URLSearchParams(); |
||||||
|
replyParams.append('message', Math.random()); |
||||||
|
replyParams.append('thread', thread); |
||||||
|
replyParams.append('captcha', '000000'); |
||||||
|
const promise2 = await fetch('http://localhost/forms/board/test/post', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: replyParams |
||||||
|
}).then(async (response2) => { |
||||||
|
expect(response2.ok).toBe(true); |
||||||
|
}); |
||||||
|
promises.push(promise2); |
||||||
|
} |
||||||
|
}); |
||||||
|
promises.push(promise); |
||||||
|
} |
||||||
|
await Promise.all(promises); //wait for all posts to go through
|
||||||
|
jest.setTimeout(5*1000); //back to normal timeout
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
@ -0,0 +1,246 @@ |
|||||||
|
const fetch = require('node-fetch'); |
||||||
|
|
||||||
|
module.exports = () => describe('login and create test board', () => { |
||||||
|
|
||||||
|
let sessionCookie |
||||||
|
, csrfToken; |
||||||
|
|
||||||
|
test('login as admin', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('username', 'admin'); |
||||||
|
params.append('password', process.env.TEST_ADMIN_PASSWORD); |
||||||
|
const response = await fetch('http://localhost/forms/login', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
const rawHeaders = response.headers.raw(); |
||||||
|
expect(rawHeaders['set-cookie']).toBeDefined(); |
||||||
|
expect(rawHeaders['set-cookie'][0]).toMatch(/^connect\.sid/); |
||||||
|
sessionCookie = rawHeaders['set-cookie'][0]; |
||||||
|
csrfToken = await fetch('http://localhost/csrf.json', { headers: { 'cookie': sessionCookie }}) |
||||||
|
.then(res => res.json()) |
||||||
|
.then(json => json.token); |
||||||
|
}); |
||||||
|
|
||||||
|
test('delete test board if exists', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('_csrf', csrfToken); |
||||||
|
params.append('uri', 'test'); |
||||||
|
params.append('confirm', 'true'); |
||||||
|
const response = await fetch('http://localhost/forms/board/test/deleteboard', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
expect([200, 404]).toContain(response.status) |
||||||
|
}); |
||||||
|
|
||||||
|
test('create test board', async () => { |
||||||
|
const params = new URLSearchParams(); |
||||||
|
params.append('uri', 'test'); |
||||||
|
params.append('name', 'test'); |
||||||
|
const response = await fetch('http://localhost/forms/create', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
expect([302, 409]).toContain(response.status) |
||||||
|
}); |
||||||
|
|
||||||
|
test('change global settings, disable antispam', async () => { |
||||||
|
const params = new URLSearchParams({ |
||||||
|
_csrf: csrfToken, |
||||||
|
allowed_hosts: '', |
||||||
|
prune_ips: '0', |
||||||
|
global_announcement: '', |
||||||
|
country_code_header: 'x-country-code', |
||||||
|
ip_header: 'x-real-ip', |
||||||
|
meta_site_name: '', |
||||||
|
meta_url: '', |
||||||
|
stats_count_anonymizers: 'true', |
||||||
|
prune_immediately: 'true', |
||||||
|
thumb_extension: '.jpg', |
||||||
|
cache_templates: 'true', |
||||||
|
lock_wait: '3000', |
||||||
|
overboard_limit: '20', |
||||||
|
overboard_catalog_limit: '100', |
||||||
|
allow_custom_overboard: 'true', |
||||||
|
archive_links: 'https://archive.today/?run=1&url=%s', |
||||||
|
reverse_links: 'https://tineye.com/search?url=%s', |
||||||
|
prune_modlogs: '30', |
||||||
|
default_ban_duration: '31536000000', |
||||||
|
quote_limit: '25', |
||||||
|
preview_replies: '5', |
||||||
|
sticky_preview_replies: '5', |
||||||
|
early_404_fraction: '3', |
||||||
|
early_404_replies: '5', |
||||||
|
max_recent_news: '5', |
||||||
|
captcha_options_type: 'text', |
||||||
|
captcha_options_grid_image_size: '120', |
||||||
|
captcha_options_grid_size: '4', |
||||||
|
captcha_options_grid_icon_y_offset: '15', |
||||||
|
captcha_options_generate_limit: '250', |
||||||
|
captcha_options_num_distorts_min: '2', |
||||||
|
captcha_options_num_distorts_max: '3', |
||||||
|
captcha_options_distortion: '7', |
||||||
|
block_bypass_force_anonymizers: 'true', |
||||||
|
block_bypass_expire_after_uses: '50', |
||||||
|
block_bypass_expire_after_time: '86400000', |
||||||
|
filters: '', |
||||||
|
strict_filtering: 'true', |
||||||
|
filter_mode: '0', |
||||||
|
ban_duration: '0', |
||||||
|
flood_timers_same_content_same_ip: '0', |
||||||
|
flood_timers_same_content_any_ip: '0', |
||||||
|
flood_timers_any_content_same_ip: '0', |
||||||
|
dnsbl_blacklists: 'tor.dan.me.uk\nzen.spamhaus.org', |
||||||
|
dnsbl_cache_time: '3600', |
||||||
|
rate_limit_cost_captcha: '10', |
||||||
|
rate_limit_cost_board_settings: '30', |
||||||
|
rate_limit_cost_edit_post: '30', |
||||||
|
highlight_options_language_subset: 'javascript\n' + |
||||||
|
'typescript\n' + |
||||||
|
'perl\n' + |
||||||
|
'js\n' + |
||||||
|
'c++\n' + |
||||||
|
'c\n' + |
||||||
|
'java\n' + |
||||||
|
'kotlin\n' + |
||||||
|
'php\n' + |
||||||
|
'h\n' + |
||||||
|
'csharp\n' + |
||||||
|
'bash\n' + |
||||||
|
'sh\n' + |
||||||
|
'zsh\n' + |
||||||
|
'python\n' + |
||||||
|
'ruby\n' + |
||||||
|
'css\n' + |
||||||
|
'html\n' + |
||||||
|
'json\n' + |
||||||
|
'golang\n' + |
||||||
|
'rust\n' + |
||||||
|
'aa', |
||||||
|
highlight_options_threshold: '5', |
||||||
|
themes: '', |
||||||
|
code_themes: '', |
||||||
|
board_defaults_theme: 'yotsuba-b', |
||||||
|
board_defaults_code_theme: 'ir-black', |
||||||
|
global_limits_custom_css_enabled: 'true', |
||||||
|
global_limits_custom_css_filters: '@\nurl(', |
||||||
|
global_limits_custom_css_strict: 'true', |
||||||
|
global_limits_custom_css_max: '10000', |
||||||
|
global_limits_field_length_name: '100', |
||||||
|
global_limits_field_length_email: '100', |
||||||
|
global_limits_field_length_subject: '100', |
||||||
|
global_limits_field_length_postpassword: '100', |
||||||
|
global_limits_field_length_message: '20000', |
||||||
|
global_limits_field_length_report_reason: '100', |
||||||
|
global_limits_field_length_ban_reason: '100', |
||||||
|
global_limits_field_length_log_message: '100', |
||||||
|
global_limits_field_length_uri: '50', |
||||||
|
global_limits_field_length_boardname: '50', |
||||||
|
global_limits_field_length_description: '100', |
||||||
|
global_limits_multi_input_posts_anon: '20', |
||||||
|
global_limits_multi_input_posts_staff: '100', |
||||||
|
frontend_script_default_embeds_enabled: 'true', |
||||||
|
frontend_script_default_hide_recursive: 'true', |
||||||
|
frontend_script_default_smooth_scrolling: 'true', |
||||||
|
frontend_script_default_thread_watcher: 'true', |
||||||
|
frontend_script_default_volume: '100', |
||||||
|
frontend_script_default_loop: 'true', |
||||||
|
frontend_script_default_image_loading_bars: 'true', |
||||||
|
frontend_script_default_live: 'true', |
||||||
|
frontend_script_default_local_time: 'true', |
||||||
|
frontend_script_default_relative_time: 'true', |
||||||
|
frontend_script_default_show_yous: 'true', |
||||||
|
frontend_script_default_notifications_yous_only: 'true', |
||||||
|
frontend_script_default_tegaki_width: '500', |
||||||
|
frontend_script_default_tegaki_height: '500', |
||||||
|
audio_thumbnails: 'true', |
||||||
|
ffmpeg_gif_thumbnails: 'true', |
||||||
|
thumb_size: '250', |
||||||
|
video_thumb_percentage: '5', |
||||||
|
other_mime_types: 'text/plain\napplication/pdf', |
||||||
|
space_file_name_replacement: '_', |
||||||
|
global_limits_reply_limit_min: '10', |
||||||
|
global_limits_reply_limit_max: '1000', |
||||||
|
global_limits_thread_limit_min: '100', |
||||||
|
global_limits_thread_limit_max: '200', |
||||||
|
global_limits_bump_limit_min: '10', |
||||||
|
global_limits_bump_limit_max: '1000', |
||||||
|
global_limits_post_files_max: '5', |
||||||
|
global_limits_post_files_size_max: '10485760', |
||||||
|
global_limits_custom_pages_max_length: '10000', |
||||||
|
global_limits_custom_pages_max: '10', |
||||||
|
global_limits_banner_files_width: '500', |
||||||
|
global_limits_banner_files_height: '500', |
||||||
|
global_limits_banner_files_size_max: '10485760', |
||||||
|
global_limits_banner_files_max: '10', |
||||||
|
global_limits_banner_files_total: '100', |
||||||
|
global_limits_flag_files_size_max: '1048576', |
||||||
|
global_limits_flag_files_max: '10', |
||||||
|
global_limits_flag_files_total: '100', |
||||||
|
global_limits_asset_files_size_max: '10485760', |
||||||
|
global_limits_asset_files_max: '10', |
||||||
|
global_limits_asset_files_total: '50', |
||||||
|
webring_proxy_address: '', |
||||||
|
webring_following: '', |
||||||
|
webring_logos: '', |
||||||
|
webring_blacklist: '', |
||||||
|
board_defaults_message_r9k_mode: '0', |
||||||
|
board_defaults_file_r9k_mode: '0', |
||||||
|
board_defaults_lock_mode: '0', |
||||||
|
board_defaults_captcha_mode: '0', |
||||||
|
board_defaults_pph_trigger: '50', |
||||||
|
board_defaults_pph_trigger_action: '2', |
||||||
|
board_defaults_tph_trigger: '10', |
||||||
|
board_defaults_tph_trigger_action: '1', |
||||||
|
board_defaults_lock_reset: '0', |
||||||
|
board_defaults_captcha_reset: '0', |
||||||
|
board_defaults_default_name: 'Anon', |
||||||
|
board_defaults_enable_tegaki: 'true', |
||||||
|
board_defaults_user_post_delete: 'true', |
||||||
|
board_defaults_user_post_spoiler: 'true', |
||||||
|
board_defaults_user_post_unlink: 'true', |
||||||
|
board_defaults_thread_limit: '100', |
||||||
|
board_defaults_reply_limit: '1000', |
||||||
|
board_defaults_bump_limit: '500', |
||||||
|
board_defaults_max_files: '5', |
||||||
|
board_defaults_min_thread_message_length: '0', |
||||||
|
board_defaults_min_reply_message_length: '0', |
||||||
|
board_defaults_max_thread_message_length: '20000', |
||||||
|
board_defaults_max_reply_message_length: '20000', |
||||||
|
board_defaults_delete_protection_count: '0', |
||||||
|
board_defaults_delete_protection_age: '0', |
||||||
|
board_defaults_filter_mode: '0', |
||||||
|
board_defaults_filter_ban_duration: '0', |
||||||
|
board_defaults_allowed_file_types_video: 'true', |
||||||
|
board_defaults_allowed_file_types_image: 'true', |
||||||
|
board_defaults_allowed_file_types_animated_image: 'true', |
||||||
|
board_defaults_allowed_file_types_audio: 'true', |
||||||
|
}); |
||||||
|
const response = await fetch('http://localhost/forms/global/settings', { |
||||||
|
headers: { |
||||||
|
'x-using-xhr': 'true', |
||||||
|
'cookie': sessionCookie, |
||||||
|
}, |
||||||
|
method: 'POST', |
||||||
|
body: params, |
||||||
|
redirect: 'manual', |
||||||
|
}); |
||||||
|
expect(response.status).toBe(200); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
Loading…
Reference in new issue