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