From 5b7b2716274052191787603b1ab52294d09dada1 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 22 Apr 2021 16:39:25 -0600 Subject: [PATCH 01/15] Add github actions --- .github/workflows/main.yml | 78 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..3e03fe5 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,78 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the master branch + push: + branches: [ master ] + pull_request: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + test_stable: + runs-on: ubuntu-latest + strategy: + matrix: + firefox: [ '73.0' ] + include: + - nim-version: 'stable' + cache-key: 'stable' + steps: + - uses: actions/checkout@v2 + - name: Checkout submodules + run: git submodule update --init --recursive + + - name: Setup firefox + uses: browser-actions/setup-firefox@latest + with: + firefox-version: ${{ matrix.firefox }} + + - name: Get Date + id: get-date + run: echo "::set-output name=date::$(date "+%Y-%m-%d")" + shell: bash + + - name: Cache choosenim + uses: actions/cache@v2 + with: + path: ~/.choosenim + key: ${{ runner.os }}-choosenim-${{ matrix.cache-key }} + + - name: Cache nimble + uses: actions/cache@v2 + with: + path: ~/.nimble + key: ${{ runner.os }}-nimble-${{ hashFiles('*.nimble') }} + + - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: "${{ matrix.nim-version }}" + + - name: Install geckodriver + run: | + sudo apt-get -qq update + sudo apt-get install autoconf libtool libsass-dev + wget https://github.com/mozilla/geckodriver/releases/download/v0.29.1/geckodriver-v0.29.1-linux64.tar.gz + mkdir geckodriver + tar -xzf geckodriver-v0.29.1-linux64.tar.gz -C geckodriver + export PATH=$PATH:$PWD/geckodriver + + - name: Install choosenim + run: | + export CHOOSENIM_CHOOSE_VERSION="stable" + curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh + sh init.sh -y + export PATH=$HOME/.nimble/bin:$PATH + nimble refresh -y + + - name: Run tests + run: | + export MOZ_HEADLESS=1 + nimble -y install + nimble -y test From 8cd5c45cda0003b6486be62a90f84e5b6daa9830 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 22 Apr 2021 16:42:32 -0600 Subject: [PATCH 02/15] Remove travis --- .travis.yml | 45 --------------------------------------------- 1 file changed, 45 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b5c6c9b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ -os: - - linux - -language: c - -cache: - directories: - - "$HOME/.nimble" - - "$HOME/.choosenim" - -addons: - firefox: "73.0" - -before_install: - - sudo apt-get -qq update - - sudo apt-get install autoconf libtool - - git clone -b 3.5.4 https://github.com/sass/libsass.git - - cd libsass - - autoreconf --force --install - - | - ./configure \ - --disable-tests \ - --disable-static \ - --enable-shared \ - --prefix=/usr - - sudo make -j5 install - - cd .. - - - wget https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz - - mkdir geckodriver - - tar -xzf geckodriver-v0.26.0-linux64.tar.gz -C geckodriver - - export PATH=$PATH:$PWD/geckodriver - -install: - - export CHOOSENIM_CHOOSE_VERSION="stable" - - | - curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh - sh init.sh -y - - export PATH=$HOME/.nimble/bin:$PATH - - nimble refresh -y - -script: - - export MOZ_HEADLESS=1 - - nimble -y install - - nimble -y test From 0055a12fc137a07b33e99c38b78b6938dab01fc4 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 24 Apr 2021 16:03:50 -0600 Subject: [PATCH 03/15] Use choosenim instead --- .github/workflows/main.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3e03fe5..81e0144 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,8 +21,7 @@ jobs: matrix: firefox: [ '73.0' ] include: - - nim-version: 'stable' - cache-key: 'stable' + - cache-key: 'stable' steps: - uses: actions/checkout@v2 - name: Checkout submodules @@ -50,10 +49,6 @@ jobs: path: ~/.nimble key: ${{ runner.os }}-nimble-${{ hashFiles('*.nimble') }} - - uses: jiro4989/setup-nim-action@v1 - with: - nim-version: "${{ matrix.nim-version }}" - - name: Install geckodriver run: | sudo apt-get -qq update @@ -73,6 +68,7 @@ jobs: - name: Run tests run: | + export PATH=$HOME/.nimble/bin:$PATH export MOZ_HEADLESS=1 nimble -y install nimble -y test From 8782dff349098e7df8e8573fed2442c36642e2f3 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 24 Apr 2021 16:19:39 -0600 Subject: [PATCH 04/15] Use matrix nim version --- .github/workflows/main.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 81e0144..64c51aa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,8 @@ jobs: matrix: firefox: [ '73.0' ] include: - - cache-key: 'stable' + - nim-version: 'stable' + cache-key: 'stable' steps: - uses: actions/checkout@v2 - name: Checkout submodules @@ -60,7 +61,7 @@ jobs: - name: Install choosenim run: | - export CHOOSENIM_CHOOSE_VERSION="stable" + export CHOOSENIM_CHOOSE_VERSION="${{ matrix.nim-version }}" curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y export PATH=$HOME/.nimble/bin:$PATH From 7954a38601116158b67a996ff154a1abc8973e49 Mon Sep 17 00:00:00 2001 From: zetashift Date: Mon, 26 Apr 2021 02:39:03 +0200 Subject: [PATCH 05/15] Pinned Threads (#278) * Added isSticky field to `Thread` and in the sql query making a Thread - Modified indices in `data` and `selectUser` to support `isSticky` - Add backend procs for initial sticky logic, modeled after locking threads - Fix indices in selectThread - Fixup posts.json's threadquery to match Thread with sticky field * Implement StickyButton for postbutton.nim and add it to postlist.nim * Fix sticky routes * Order sticky in a way that they actually appear at the top * Add border for isSticky on genThread * Rename stickies to pinned, so professional! * Add pinned tests - Add an id to pin button, and add first attempt at useful tests - Improve pin tests, refactored it into adminTests and userTests - Add an id to pin button, and add first attempt at useful tests - Improve pin tests, refactored it into adminTests and userTests * Make tests more reliable Co-authored-by: Joey Yakimowich-Payne --- src/forum.nim | 51 ++++++++++++++++++++++----- src/frontend/post.nim | 2 +- src/frontend/postbutton.nim | 59 +++++++++++++++++++++++++++++++- src/frontend/postlist.nim | 3 ++ src/frontend/threadlist.nim | 10 ++++-- src/setup_nimforum.nim | 1 + tests/browsertests/common.nim | 7 ++-- tests/browsertests/scenario1.nim | 2 +- tests/browsertests/threads.nim | 53 +++++++++++++++++++++++++++- 9 files changed, 170 insertions(+), 18 deletions(-) diff --git a/src/forum.nim b/src/forum.nim index c85ad2e..b87223d 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -400,10 +400,10 @@ proc selectThread(threadRow: seq[string], author: User): Thread = id: threadRow[0].parseInt, topic: threadRow[1], category: Category( - id: threadRow[5].parseInt, - name: threadRow[6], - description: threadRow[7], - color: threadRow[8] + id: threadRow[6].parseInt, + name: threadRow[7], + description: threadRow[8], + color: threadRow[9] ), users: @[], replies: posts[0].parseInt-1, @@ -412,6 +412,7 @@ proc selectThread(threadRow: seq[string], author: User): Thread = creation: posts[1].parseInt, isLocked: threadRow[4] == "1", isSolved: false, # TODO: Add a field to `post` to identify the solution. + isPinned: threadRow[5] == "1" ) # Gather the users list. @@ -709,6 +710,13 @@ proc executeLockState(c: TForumData, threadId: int, locked: bool) = # Save the like. exec(db, crud(crUpdate, "thread", "isLocked"), locked.int, threadId) +proc executePinState(c: TForumData, threadId: int, pinned: bool) = + if c.rank < Moderator: + raise newForumError("You do not have permission to pin this thread.") + + # (Un)pin this thread + exec(db, crud(crUpdate, "thread", "isPinned"), pinned.int, threadId) + proc executeDeletePost(c: TForumData, postId: int) = # Verify that this post belongs to the user. const postQuery = sql""" @@ -833,7 +841,7 @@ routes: categoryArgs.insert($categoryId, 0) const threadsQuery = - """select t.id, t.name, views, strftime('%s', modified), isLocked, + """select t.id, t.name, views, strftime('%s', modified), isLocked, isPinned, c.id, c.name, c.description, c.color, u.id, u.name, u.email, strftime('%s', u.lastOnline), strftime('%s', u.previousVisitAt), u.status, u.isDeleted @@ -846,14 +854,14 @@ routes: order by p.author limit 1 ) - order by modified desc limit ?, ?;""" + order by isPinned desc, modified desc limit ?, ?;""" let thrCount = getValue(db, countQuery, countArgs).parseInt() let moreCount = max(0, thrCount - (start + count)) var list = ThreadList(threads: @[], moreCount: moreCount) for data in getAllRows(db, sql(threadsQuery % categorySection), categoryArgs): - let thread = selectThread(data[0 .. 8], selectUser(data[9 .. ^1])) + let thread = selectThread(data[0 .. 9], selectUser(data[10 .. ^1])) list.threads.add(thread) resp $(%list), "application/json" @@ -868,7 +876,7 @@ routes: count = 10 const threadsQuery = - sql"""select t.id, t.name, views, strftime('%s', modified), isLocked, + sql"""select t.id, t.name, views, strftime('%s', modified), isLocked, isPinned, c.id, c.name, c.description, c.color from thread t, category c where t.id = ? and isDeleted = 0 and category = c.id;""" @@ -1339,6 +1347,33 @@ routes: except ForumError as exc: resp Http400, $(%exc.data), "application/json" + post re"/(pin|unpin)": + createTFD() + if not c.loggedIn(): + let err = PostError( + errorFields: @[], + message: "Not logged in." + ) + resp Http401, $(%err), "application/json" + + let formData = request.formData + cond "id" in formData + + let threadId = getInt(formData["id"].body, -1) + cond threadId != -1 + + try: + case request.path + of "/pin": + executePinState(c, threadId, true) + of "/unpin": + executePinState(c, threadId, false) + else: + assert false + resp Http200, "{}", "application/json" + except ForumError as exc: + resp Http400, $(%exc.data), "application/json" + post re"/delete(Post|Thread)": createTFD() if not c.loggedIn(): diff --git a/src/frontend/post.nim b/src/frontend/post.nim index 0530814..dc12e47 100644 --- a/src/frontend/post.nim +++ b/src/frontend/post.nim @@ -64,4 +64,4 @@ when defined(js): renderPostUrl(thread.id, post.id) proc renderPostUrl*(link: PostLink): string = - renderPostUrl(link.threadId, link.postId) \ No newline at end of file + renderPostUrl(link.threadId, link.postId) diff --git a/src/frontend/postbutton.nim b/src/frontend/postbutton.nim index 8bd9c34..98f8d92 100644 --- a/src/frontend/postbutton.nim +++ b/src/frontend/postbutton.nim @@ -201,4 +201,61 @@ when defined(js): text " Unlock Thread" else: italic(class="fas fa-lock") - text " Lock Thread" \ No newline at end of file + text " Lock Thread" + + type + PinButton* = ref object + error: Option[PostError] + loading: bool + + proc newPinButton*(): PinButton = + PinButton() + + proc onPost(httpStatus: int, response: kstring, state: PinButton, + thread: var Thread) = + postFinished: + thread.isPinned = not thread.isPinned + + proc onPinClick(ev: Event, n: VNode, state: PinButton, thread: var Thread) = + if state.loading: return + + state.loading = true + state.error = none[PostError]() + + # Same as LockButton so the following is still a hack and karax should support this. + var formData = newFormData() + formData.append("id", $thread.id) + let uri = + if thread.isPinned: + makeUri("/unpin") + else: + makeUri("/pin") + ajaxPost(uri, @[], formData.to(cstring), + (s: int, r: kstring) => onPost(s, r, state, thread)) + + ev.preventDefault() + + proc render*(state: PinButton, thread: var Thread, + currentUser: Option[User]): VNode = + if currentUser.isNone() or + currentUser.get().rank < Moderator: + return buildHtml(tdiv()) + + let tooltip = + if state.error.isSome(): state.error.get().message + else: "" + + result = buildHtml(): + button(class="btn btn-secondary", id="pin-btn", + onClick=(e: Event, n: VNode) => + onPinClick(e, n, state, thread), + "data-tooltip"=tooltip, + onmouseleave=(e: Event, n: VNode) => + (state.error = none[PostError]())): + if thread.isPinned: + italic(class="fas fa-thumbtack") + text " Unpin Thread" + else: + italic(class="fas fa-thumbtack") + text " Pin Thread" + diff --git a/src/frontend/postlist.nim b/src/frontend/postlist.nim index 7da57c1..66b3162 100644 --- a/src/frontend/postlist.nim +++ b/src/frontend/postlist.nim @@ -36,6 +36,7 @@ when defined(js): likeButton: LikeButton deleteModal: DeleteModal lockButton: LockButton + pinButton: PinButton categoryPicker: CategoryPicker proc onReplyPosted(id: int) @@ -56,6 +57,7 @@ when defined(js): likeButton: newLikeButton(), deleteModal: newDeleteModal(onDeletePost, onDeleteThread, nil), lockButton: newLockButton(), + pinButton: newPinButton(), categoryPicker: newCategoryPicker(onCategoryChanged) ) @@ -411,6 +413,7 @@ when defined(js): text " Reply" render(state.lockButton, list.thread, currentUser) + render(state.pinButton, list.thread, currentUser) render(state.replyBox, list.thread, state.replyingTo, false) diff --git a/src/frontend/threadlist.nim b/src/frontend/threadlist.nim index e0b2d36..ecec6da 100644 --- a/src/frontend/threadlist.nim +++ b/src/frontend/threadlist.nim @@ -15,6 +15,7 @@ type creation*: int64 ## Unix timestamp isLocked*: bool isSolved*: bool + isPinned*: bool ThreadList* = ref object threads*: seq[Thread] @@ -96,15 +97,18 @@ when defined(js): else: return $duration.inSeconds & "s" - proc genThread(thread: Thread, isNew: bool, noBorder: bool, displayCategory=true): VNode = + proc genThread(pos: int, thread: Thread, isNew: bool, noBorder: bool, displayCategory=true): VNode = let isOld = (getTime() - thread.creation.fromUnix).inWeeks > 2 let isBanned = thread.author.rank.isBanned() result = buildHtml(): - tr(class=class({"no-border": noBorder, "banned": isBanned})): + tr(class=class({"no-border": noBorder, "banned": isBanned, "pinned": thread.isPinned, "thread-" & $pos: true})): td(class="thread-title"): if thread.isLocked: italic(class="fas fa-lock fa-xs", title="Thread cannot be replied to") + if thread.isPinned: + italic(class="fas fa-thumbtack fa-xs", + title="Pinned post") if isBanned: italic(class="fas fa-ban fa-xs", title="Thread author is banned") @@ -223,7 +227,7 @@ when defined(js): let isLastThread = i+1 == list.threads.len let (isLastUnseen, isNew) = getInfo(list.threads, i, currentUser) - genThread(thread, isNew, + genThread(i+1, thread, isNew, noBorder=isLastUnseen or isLastThread, displayCategory=displayCategory) if isLastUnseen and (not isLastThread): diff --git a/src/setup_nimforum.nim b/src/setup_nimforum.nim index 34511fa..c80ad3b 100644 --- a/src/setup_nimforum.nim +++ b/src/setup_nimforum.nim @@ -81,6 +81,7 @@ proc initialiseDb(admin: tuple[username, password, email: string], isLocked boolean not null default 0, solution integer, isDeleted boolean not null default 0, + isPinned boolean not null default 0, foreign key (category) references category(id), foreign key (solution) references post(id) diff --git a/tests/browsertests/common.nim b/tests/browsertests/common.nim index d906675..e5924f3 100644 --- a/tests/browsertests/common.nim +++ b/tests/browsertests/common.nim @@ -30,7 +30,8 @@ proc elementIsSome(element: Option[Element]): bool = proc elementIsNone(element: Option[Element]): bool = return element.isNone -proc waitForElement*(session: Session, selector: string, strategy=CssSelector, timeout=20000, pollTime=50, waitCondition=elementIsSome): Option[Element] +proc waitForElement*(session: Session, selector: string, strategy=CssSelector, timeout=20000, pollTime=50, + waitCondition: proc(element: Option[Element]): bool = elementIsSome): Option[Element] proc click*(session: Session, element: string, strategy=CssSelector) = let el = session.waitForElement(element, strategy) @@ -71,14 +72,14 @@ proc setColor*(session: Session, element, color: string, strategy=CssSelector) = proc checkIsNone*(session: Session, element: string, strategy=CssSelector) = discard session.waitForElement(element, strategy, waitCondition=elementIsNone) -proc checkText*(session: Session, element, expectedValue: string) = +template checkText*(session: Session, element, expectedValue: string) = let el = session.waitForElement(element) check el.get().getText() == expectedValue proc waitForElement*( session: Session, selector: string, strategy=CssSelector, timeout=20000, pollTime=50, - waitCondition=elementIsSome + waitCondition: proc(element: Option[Element]): bool = elementIsSome ): Option[Element] = var waitTime = 0 diff --git a/tests/browsertests/scenario1.nim b/tests/browsertests/scenario1.nim index 6e10e4c..dc78007 100644 --- a/tests/browsertests/scenario1.nim +++ b/tests/browsertests/scenario1.nim @@ -40,4 +40,4 @@ proc test*(session: Session, baseUrl: string) = register "TEst1", "test1", verify = false ensureExists "#signup-form .has-error" - navigate baseUrl \ No newline at end of file + navigate baseUrl diff --git a/tests/browsertests/threads.nim b/tests/browsertests/threads.nim index e80b9c0..2cbecb5 100644 --- a/tests/browsertests/threads.nim +++ b/tests/browsertests/threads.nim @@ -58,10 +58,22 @@ proc userTests(session: Session, baseUrl: string) = # Make sure the forum post is gone checkIsNone "To be deleted", LinkTextSelector + test "cannot (un)pin thread": + with session: + navigate(baseUrl) + + click "#new-thread-btn" + + sendKeys "#thread-title", "Unpinnable" + sendKeys "#reply-textarea", "Cannot (un)pin as an user" + + click "#create-thread-btn" + + checkIsNone "#pin-btn" + session.logout() proc anonymousTests(session: Session, baseUrl: string) = - suite "anonymous user tests": with session: navigate baseUrl @@ -161,6 +173,45 @@ proc adminTests(session: Session, baseUrl: string) = # Make sure the forum post is gone checkIsNone adminTitleStr, LinkTextSelector + test "Can pin a thread": + with session: + click "#new-thread-btn" + sendKeys "#thread-title", "Pinned post" + sendKeys "#reply-textarea", "A pinned post" + click "#create-thread-btn" + + navigate(baseUrl) + click "#new-thread-btn" + sendKeys "#thread-title", "Normal post" + sendKeys "#reply-textarea", "A normal post" + click "#create-thread-btn" + + navigate(baseUrl) + click "Pinned post", LinkTextSelector + click "#pin-btn" + checkText "#pin-btn", "Unpin Thread" + + navigate(baseUrl) + + # Make sure pin exists + ensureExists "#threads-list .thread-1 .thread-title i" + + checkText "#threads-list .thread-1 .thread-title a", "Pinned post" + checkText "#threads-list .thread-2 .thread-title a", "Normal post" + + test "Can unpin a thread": + with session: + click "Pinned post", LinkTextSelector + click "#pin-btn" + checkText "#pin-btn", "Pin Thread" + + navigate(baseUrl) + + checkIsNone "#threads-list .thread-2 .thread-title i" + + checkText "#threads-list .thread-1 .thread-title a", "Normal post" + checkText "#threads-list .thread-2 .thread-title a", "Pinned post" + session.logout() proc test*(session: Session, baseUrl: string) = From 35e0de7b91fc76230574b2882005d1eccd12e2ad Mon Sep 17 00:00:00 2001 From: zetashift Date: Tue, 27 Apr 2021 18:23:48 +0200 Subject: [PATCH 06/15] Tests for locking threads (#284) * Initial try at locking threads tests * Uncomment tests * Consist casing * Add correct query * Remove redundant navigate call and add frontpage check * Improve locked thread on frontpage test --- src/frontend/postbutton.nim | 2 +- tests/browsertests/threads.nim | 43 +++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/frontend/postbutton.nim b/src/frontend/postbutton.nim index 98f8d92..9fa4ab4 100644 --- a/src/frontend/postbutton.nim +++ b/src/frontend/postbutton.nim @@ -190,7 +190,7 @@ when defined(js): else: "" result = buildHtml(): - button(class="btn btn-secondary", + button(class="btn btn-secondary", id="lock-btn", onClick=(e: Event, n: VNode) => onLockClick(e, n, state, thread), "data-tooltip"=tooltip, diff --git a/tests/browsertests/threads.nim b/tests/browsertests/threads.nim index 2cbecb5..32ce686 100644 --- a/tests/browsertests/threads.nim +++ b/tests/browsertests/threads.nim @@ -1,5 +1,4 @@ import unittest, common - import webdriver let @@ -71,6 +70,19 @@ proc userTests(session: Session, baseUrl: string) = checkIsNone "#pin-btn" + test "cannot lock threads": + with session: + navigate(baseUrl) + + click "#new-thread-btn" + + sendKeys "#thread-title", "Locking" + sendkeys "#reply-textarea", "Cannot lock as an user" + + click "#create-thread-btn" + + checkIsNone "#lock-btn" + session.logout() proc anonymousTests(session: Session, baseUrl: string) = @@ -173,7 +185,7 @@ proc adminTests(session: Session, baseUrl: string) = # Make sure the forum post is gone checkIsNone adminTitleStr, LinkTextSelector - test "Can pin a thread": + test "can pin a thread": with session: click "#new-thread-btn" sendKeys "#thread-title", "Pinned post" @@ -199,7 +211,7 @@ proc adminTests(session: Session, baseUrl: string) = checkText "#threads-list .thread-1 .thread-title a", "Pinned post" checkText "#threads-list .thread-2 .thread-title a", "Normal post" - test "Can unpin a thread": + test "can unpin a thread": with session: click "Pinned post", LinkTextSelector click "#pin-btn" @@ -212,6 +224,31 @@ proc adminTests(session: Session, baseUrl: string) = checkText "#threads-list .thread-1 .thread-title a", "Normal post" checkText "#threads-list .thread-2 .thread-title a", "Pinned post" + test "can lock a thread": + with session: + click "Locking", LinkTextSelector + click "#lock-btn" + + ensureExists "#thread-title i.fas.fa-lock.fa-xs" + + test "locked thread appears on frontpage": + with session: + click "#new-thread-btn" + sendKeys "#thread-title", "A new locked thread" + sendKeys "#reply-textarea", "This thread should appear locked on the frontpage" + click "#create-thread-btn" + click "#lock-btn" + + navigate(baseUrl) + ensureExists "#threads-list .thread-1 .thread-title i.fas.fa-lock.fa-xs" + + test "can unlock a thread": + with session: + click "Locking", LinkTextSelector + click "#lock-btn" + + checkIsNone "#thread-title i.fas.fa-lock.fa-xs" + session.logout() proc test*(session: Session, baseUrl: string) = From a1601b4600c7d96dd85e30d05a96d5c41d64b538 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sun, 16 May 2021 20:04:01 -0300 Subject: [PATCH 07/15] Use input type search on search instead of text (#291) --- src/frontend/header.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/header.nim b/src/frontend/header.nim index cde48de..7cfb133 100644 --- a/src/frontend/header.nim +++ b/src/frontend/header.nim @@ -96,8 +96,8 @@ when defined(js): section(class="navbar-section"): tdiv(class="input-group input-inline"): input(class="search-input input-sm", - `type`="text", placeholder="search", - id="search-box", + `type`="search", placeholder="Search", + id="search-box", required="required", onKeyDown=onKeyDown) if state.loading: tdiv(class="loading") From f940f8186189813eb928d9c895f6c4a972a8c907 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 11 Nov 2021 22:20:54 +0000 Subject: [PATCH 08/15] Lock CI Nim ver and update to Nim 1.6.0. --- .github/workflows/main.yml | 14 +++++++------- src/forum.nim | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 64c51aa..9b02e14 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,7 @@ name: CI -# Controls when the action will run. +# Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the master branch push: @@ -21,13 +21,13 @@ jobs: matrix: firefox: [ '73.0' ] include: - - nim-version: 'stable' + - nim-version: '1.6.0' cache-key: 'stable' steps: - uses: actions/checkout@v2 - name: Checkout submodules run: git submodule update --init --recursive - + - name: Setup firefox uses: browser-actions/setup-firefox@latest with: @@ -37,13 +37,13 @@ jobs: id: get-date run: echo "::set-output name=date::$(date "+%Y-%m-%d")" shell: bash - + - name: Cache choosenim uses: actions/cache@v2 with: path: ~/.choosenim key: ${{ runner.os }}-choosenim-${{ matrix.cache-key }} - + - name: Cache nimble uses: actions/cache@v2 with: @@ -58,7 +58,7 @@ jobs: mkdir geckodriver tar -xzf geckodriver-v0.29.1-linux64.tar.gz -C geckodriver export PATH=$PATH:$PWD/geckodriver - + - name: Install choosenim run: | export CHOOSENIM_CHOOSE_VERSION="${{ matrix.nim-version }}" @@ -66,7 +66,7 @@ jobs: sh init.sh -y export PATH=$HOME/.nimble/bin:$PATH nimble refresh -y - + - name: Run tests run: | export PATH=$HOME/.nimble/bin:$PATH diff --git a/src/forum.nim b/src/forum.nim index b87223d..60c047c 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -276,7 +276,7 @@ template createTFD() = new(c) init(c) c.req = request - if request.cookies.len > 0: + if cookies(request).len > 0: checkLoggedIn(c) #[ DB functions. TODO: Move to another module? ]# From c4684155f5741644c57558882129392c8a2112ec Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 11 Nov 2021 22:24:05 +0000 Subject: [PATCH 09/15] Run CI on all branches and every week. --- .github/workflows/main.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9b02e14..b2044db 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,14 +4,11 @@ name: CI # Controls when the action will run. on: - # Triggers the workflow on push or pull request events but only for the master branch push: - branches: [ master ] pull_request: - branches: [ master ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: + branches: [master] + schedule: + - cron: '0 0 * * 1' # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: From 40d7b1cf02aa097aa0c0a56c0d1c1b26e1c01a0a Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 22 Nov 2021 02:40:04 +0300 Subject: [PATCH 10/15] Fixes a few crashes and search functionality. (#307) * Fixes a few crashes and search functionality. * Use PostError --- src/forum.nim | 14 ++++++++++++-- src/fts.sql | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/forum.nim b/src/forum.nim index 60c047c..c2682eb 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -882,6 +882,11 @@ routes: where t.id = ? and isDeleted = 0 and category = c.id;""" let threadRow = getRow(db, threadsQuery, id) + if threadRow[0].len == 0: + let err = PostError( + message: "Specified thread does not exist" + ) + resp Http404, $(%err), "application/json" let thread = selectThread(threadRow, selectThreadAuthor(id)) let postsQuery = @@ -927,9 +932,14 @@ routes: get "/specific_posts.json": createTFD() - var + var ids: JsonNode + try: ids = parseJson(@"ids") - + except JsonParsingError: + let err = PostError( + message: "Invalid JSON in the `ids` parameter" + ) + resp Http400, $(%err), "application/json" cond ids.kind == JArray let intIDs = ids.elems.map(x => x.getInt()) let postsQuery = sql(""" diff --git a/src/fts.sql b/src/fts.sql index ee6f1af..1590a05 100644 --- a/src/fts.sql +++ b/src/fts.sql @@ -7,6 +7,7 @@ SELECT post_id, post_content, cdate, + person.id, person.name AS author, person.email AS email, strftime('%s', person.lastOnline) AS lastOnline, From ceb04561cdc2f6c32a1635d221d99564d1896dd1 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 22 Apr 2021 16:13:20 -0600 Subject: [PATCH 11/15] Create main github actions file --- .github/workflows/main.yml | 49 +++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b2044db..47a6a71 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,29 +2,30 @@ name: CI -# Controls when the action will run. +# Controls when the action will run. on: + # Triggers the workflow on push or pull request events but only for the master branch push: + branches: [ master ] pull_request: - branches: [master] - schedule: - - cron: '0 0 * * 1' + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - test_stable: + test_devel: runs-on: ubuntu-latest strategy: matrix: firefox: [ '73.0' ] include: - - nim-version: '1.6.0' + - nim-version: 'stable' cache-key: 'stable' steps: - uses: actions/checkout@v2 - - name: Checkout submodules - run: git submodule update --init --recursive - + - name: Setup firefox uses: browser-actions/setup-firefox@latest with: @@ -34,39 +35,53 @@ jobs: id: get-date run: echo "::set-output name=date::$(date "+%Y-%m-%d")" shell: bash - + - name: Cache choosenim uses: actions/cache@v2 with: path: ~/.choosenim key: ${{ runner.os }}-choosenim-${{ matrix.cache-key }} - + - name: Cache nimble uses: actions/cache@v2 with: path: ~/.nimble key: ${{ runner.os }}-nimble-${{ hashFiles('*.nimble') }} + - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: "${{ matrix.nim-version }}" + - name: Install geckodriver run: | sudo apt-get -qq update - sudo apt-get install autoconf libtool libsass-dev + sudo apt-get install autoconf libtool + git clone -b 3.5.4 https://github.com/sass/libsass.git + cd libsass + autoreconf --force --install + ./configure \ + --disable-tests \ + --disable-static \ + --enable-shared \ + --prefix=/usr + sudo make -j5 install + cd .. + wget https://github.com/mozilla/geckodriver/releases/download/v0.29.1/geckodriver-v0.29.1-linux64.tar.gz - mkdir geckodriver tar -xzf geckodriver-v0.29.1-linux64.tar.gz -C geckodriver export PATH=$PATH:$PWD/geckodriver - + - name: Install choosenim run: | - export CHOOSENIM_CHOOSE_VERSION="${{ matrix.nim-version }}" + export CHOOSENIM_CHOOSE_VERSION="stable" curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y export PATH=$HOME/.nimble/bin:$PATH nimble refresh -y - + - name: Run tests run: | - export PATH=$HOME/.nimble/bin:$PATH export MOZ_HEADLESS=1 nimble -y install nimble -y test + From 9994fc7840070b4327f54501cdea495c4c19749d Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 22 Apr 2021 16:14:51 -0600 Subject: [PATCH 12/15] test_devel -> test_stable --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 47a6a71..0402c51 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - test_devel: + test_stable: runs-on: ubuntu-latest strategy: matrix: From 4f00ea5942b0d94e70e00548ca229466b4d0ca55 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 22 Apr 2021 16:19:25 -0600 Subject: [PATCH 13/15] Add mkdir --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0402c51..fd753de 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -68,6 +68,7 @@ jobs: cd .. wget https://github.com/mozilla/geckodriver/releases/download/v0.29.1/geckodriver-v0.29.1-linux64.tar.gz + mkdir geckodriver tar -xzf geckodriver-v0.29.1-linux64.tar.gz -C geckodriver export PATH=$PATH:$PWD/geckodriver From 4a181e9cb19cc44f57871743413c3ff5086f18ec Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 22 Apr 2021 16:23:39 -0600 Subject: [PATCH 14/15] Install libsass with apt --- .github/workflows/main.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fd753de..77dd1d7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -55,17 +55,7 @@ jobs: - name: Install geckodriver run: | sudo apt-get -qq update - sudo apt-get install autoconf libtool - git clone -b 3.5.4 https://github.com/sass/libsass.git - cd libsass - autoreconf --force --install - ./configure \ - --disable-tests \ - --disable-static \ - --enable-shared \ - --prefix=/usr - sudo make -j5 install - cd .. + sudo apt-get install autoconf libtool libsass-dev wget https://github.com/mozilla/geckodriver/releases/download/v0.29.1/geckodriver-v0.29.1-linux64.tar.gz mkdir geckodriver From 88c20113232f4598dfc5cd4928d2eb3def724a94 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 22 Apr 2021 16:29:10 -0600 Subject: [PATCH 15/15] Update submodules --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 77dd1d7..fde1b09 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,6 +25,8 @@ jobs: cache-key: 'stable' steps: - uses: actions/checkout@v2 + - name: Checkout submodules + run: git submodule update --init --recursive - name: Setup firefox uses: browser-actions/setup-firefox@latest