├── .github ├── assets │ ├── exercises.png │ └── tracks.png └── workflows │ └── ci.yml ├── .gitignore ├── Eask ├── Makefile ├── README.org ├── exercism-modern.el ├── icons ├── easy.svg ├── false.svg ├── hard.svg ├── medium.svg ├── star.svg └── true.svg └── test ├── exercism-modern-test.el └── test-helper.el /.github/assets/exercises.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elken/exercism-modern/e5b492e188d63ed7e3265ebd84b60d4614149790/.github/assets/exercises.png -------------------------------------------------------------------------------- /.github/assets/tracks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elken/exercism-modern/e5b492e188d63ed7e3265ebd84b60d4614149790/.github/assets/tracks.png -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | test: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | os: [ubuntu-latest] 18 | emacs-version: 19 | - 27.1 20 | - 27.2 21 | - 28.1 22 | - 28.2 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | 27 | - uses: jcs090218/setup-emacs@master 28 | with: 29 | version: ${{ matrix.emacs-version }} 30 | 31 | - uses: ConorMacBride/install-package@v1 32 | with: 33 | brew: exercism 34 | apt: tar 35 | choco: exercism-io-cli 36 | 37 | - name: Install Exercism 38 | if: matrix.os == 'ubuntu-latest' 39 | run: | 40 | wget https://github.com/exercism/cli/releases/download/v3.1.0/exercism-3.1.0-linux-x86_64.tar.gz 41 | tar -xf exercism-3.1.0-linux-x86_64.tar.gz 42 | mv exercism /usr/local/bin 43 | 44 | - uses: actions/setup-node@v2 45 | with: 46 | node-version: "14" 47 | 48 | - uses: emacs-eask/setup-eask@master 49 | with: 50 | version: "snapshot" 51 | 52 | - name: Setup Exercism 53 | env: 54 | config: ${{ secrets.EXERCISM_CONFIG }} 55 | run: | 56 | mkdir -p ~/.config/exercism 57 | echo "$config" >> ~/.config/exercism/user.json 58 | exercism troubleshoot 59 | 60 | - name: Set workaround env var 61 | run: | 62 | echo "ACTIONS_ALLOW_UNSECURE_COMMANDS=true" >> $GITHUB_ENV 63 | 64 | - name: Run tests 65 | run: "make ci" 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.har 2 | /.eask/ 3 | -------------------------------------------------------------------------------- /Eask: -------------------------------------------------------------------------------- 1 | (package "exercism-modern" "0.0.1" "Modern interface for exercism") 2 | (website-url "https://github.com/elken/exercism-modern") 3 | 4 | (package-file "exercism-modern.el") 5 | 6 | (files "*.el") 7 | 8 | (source "gnu") 9 | (source "melpa") 10 | 11 | (depends-on "emacs" "26.1") 12 | (depends-on "request") 13 | (depends-on "async") 14 | (depends-on "tablist") 15 | 16 | (development 17 | (depends-on "f") 18 | (depends-on "undercover") 19 | (depends-on "buttercup")) 20 | 21 | (setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/usr/bin/env bash 2 | 3 | EMACS ?= emacs 4 | EASK ?= eask 5 | 6 | build: 7 | $(EASK) package 8 | $(EASK) install 9 | 10 | ci: clean build compile checkdoc lint test 11 | 12 | compile: 13 | @echo "Compiling..." 14 | $(EASK) compile 15 | 16 | checkdoc: 17 | $(EASK) lint checkdoc 18 | 19 | lint: 20 | @echo "package linting..." 21 | $(EASK) lint package 22 | 23 | test: 24 | $(EASK) install-deps --dev 25 | $(EASK) exec buttercup -L . 26 | 27 | clean: 28 | $(EASK) clean-all 29 | 30 | # Allow args to make commands 31 | %: 32 | @: 33 | 34 | .PHONY : ci compile checkdoc lint test clean tag 35 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+title: Exercism-modern 2 | #+author: Ellis Kenyő 3 | #+date: 2022-09-16 4 | #+latex_class: chameleon 5 | 6 | [[file:https://github.com/elken/exercism-modern/actions/workflows/ci.yml/badge.svg]] 7 | 8 | A modern interface to [[https://exercism.org][Exercism]]. 9 | 10 | * Installation 11 | ** Standard 12 | Clone the repo, add to your load-path 13 | ** Doom Emacs 14 | Append the following to the corresponding file: 15 | 16 | *** =packages.el= 17 | #+begin_src emacs-lisp 18 | (package! exercism-modern 19 | :recipe (:files (:defaults "icons") 20 | :host github :repo "elken/exercism-modern")) 21 | #+end_src 22 | 23 | *** =config.el= 24 | #+begin_src emacs-lisp 25 | (use-package! exercism-modern 26 | :commands (exercism-modern-jump exercism-modern-view-tracks)) 27 | #+end_src 28 | 29 | * Exercism setup 30 | Currently depends on following the setup mapped out [[https://exercism.org/docs/using/solving-exercises/working-locally][here]], after doing them you 31 | should be fine to use this. 32 | 33 | *NOTE* the first time you view the tracks/a specific track there will be a 34 | one-time delay while the icons are downloaded. This will only occur once per the 35 | main tracks view and one per track 36 | 37 | * Commands 38 | ** =exercism-modern-jump= 39 | Open dired at the exercism workspace directory. 40 | ** =exercism-modern-view-tracks= 41 | [[file:.github/assets/tracks.png]] 42 | 43 | Main entry point into Exercism; a listing of all the available programming languages that have courses. 44 | 45 | Shows your current statistics for overall completion of a track. 46 | 47 | Pressing =RET= on a track will open a listing of all the exercises available. 48 | ** =exercism-modern-track-view-exercises= 49 | [[file:.github/assets/exercises.png]] 50 | 51 | Listing of all exercises available for a given track. 52 | 53 | Any exercises not yet unlocked are greyed out. 54 | 55 | | key | action | 56 | |-----+-----------------------------------| 57 | | =RET= | Download exercise(s) | 58 | | =m= | Mark an exercise to be downloaded | 59 | | =u= | Unmark an exercise | 60 | | =t= | Toggle all marks | 61 | 62 | A more detailed listing of shortcuts available when navigating exercises is available [[https://github.com/politza/tablist][here]]. 63 | 64 | ** =exercism-modern-submit= 65 | Once you're done with an exercise and you're happy the tests pass, invoke this to submit the solution files to exercism. 66 | When invoked with the universal argument =C-u= (=SPC u= for Doom Emacs users), prompt for a buffer to submit instead. 67 | -------------------------------------------------------------------------------- /exercism-modern.el: -------------------------------------------------------------------------------- 1 | ;;; exercism-modern.el --- Modern interface for exercism -*- lexical-binding: t; -*- 2 | ;; 3 | ;; Copyright (C) 2022 Ellis Kenyő 4 | ;; 5 | ;; Author: Ellis Kenyő 6 | ;; Maintainer: Ellis Kenyő 7 | ;; SPDX-License-Identifier: GPL-3.0-or-later 8 | ;; Created: September 15, 2022 9 | ;; Modified: September 15, 2022 10 | ;; Version: 0.0.1 11 | ;; Homepage: https://github.com/elken/exercism-modern 12 | ;; Package-Requires: ((emacs "27.1") (request "0.2.0") (async "1.9.3") (tablist "1.0")) 13 | ;; 14 | ;; This file is not part of GNU Emacs. 15 | ;; 16 | ;;; Commentary: 17 | ;; 18 | ;; Modern interface for exercism 19 | ;; 20 | ;; Maps out most of the web interface to be usable in Emacs. 21 | ;; 22 | ;; `exercism-modern-jump' => open a Dired buffer in the exercism workspace 23 | ;; `exercism-modern-view-tracks' => open a buffer of all the available tracks, which can be selected with RET 24 | ;; `exercism-modern-track-view-exercises' => open a buffer of all available exercises for the last selected track 25 | ;; `exercism-modern-submit' => Submit the solution files to exercism. Invoke with universal argument to pick a buffer. 26 | ;; 27 | ;;; Code: 28 | 29 | (require 'xdg) 30 | (require 'async) 31 | (require 'request) 32 | (require 'image) 33 | (require 'tablist) 34 | 35 | (defgroup exercism-modern nil 36 | "Settings related to exercism." 37 | :group 'external 38 | :link '(url-link :tag "Homepage" "https://github.com/elken/exercism-modern")) 39 | 40 | (defgroup exercism-modern-faces nil 41 | "Faces related to exercism." 42 | :group 'exercism-modern) 43 | 44 | (defcustom exercism-modern-api-url "https://exercism.org/api/v2" 45 | "Default url to query resources for." 46 | :group 'exercism-modern 47 | :type 'string) 48 | 49 | (defcustom exercism-modern-config-file (expand-file-name "exercism/user.json" (xdg-config-home)) 50 | "Default path to the exercism config file." 51 | :group 'exercism-modern 52 | :type 'string) 53 | 54 | (defcustom exercism-modern-command (executable-find "exercism") 55 | "Exercism command to run. 56 | Defaults to first entry in $PATH, can be overridden if required." 57 | :group 'exercism-modern 58 | :type 'string) 59 | 60 | (defcustom exercism-modern-cache-dir (expand-file-name "exercism" (xdg-cache-home)) 61 | "Directory to use for caching resources." 62 | :group 'exercism-modern 63 | :type 'string) 64 | 65 | (defcustom exercism-modern-missing-icon "https://d24y9kuxp2d7l2.cloudfront.net/assets/graphics/missing-exercise-54cf5afe4add37d9cf717793c91b088a7dd242ef.svg" 66 | "URL to icon to use for missing icons." 67 | :group 'exercism-modern 68 | :type 'string) 69 | 70 | (defvar exercism-modern--icon-urls nil 71 | "Alist of (slug . iconUrl).") 72 | 73 | (defvar exercism-modern-current-track nil 74 | "Current track to pull exercises for.") 75 | 76 | (defvar exercism-modern--load-path (file-name-directory load-file-name) 77 | "Load path for the package so as to find media files.") 78 | 79 | (defun exercism-modern--load-from-package-root (path) 80 | "Load PATH relative to the package directory." 81 | (expand-file-name 82 | path 83 | (file-name-directory exercism-modern--load-path))) 84 | 85 | (defcustom exercism-modern-star-icon (exercism-modern--load-from-package-root "icons/star.svg") 86 | "Path to icon to use for recommended exercises." 87 | :type 'file 88 | :group 'exercism-modern) 89 | 90 | (defcustom exercism-modern-easy-icon (exercism-modern--load-from-package-root "icons/easy.svg") 91 | "Path to icon to use for difficulty level easy exercises." 92 | :type 'file 93 | :group 'exercism-modern) 94 | 95 | (defcustom exercism-modern-medium-icon (exercism-modern--load-from-package-root "icons/medium.svg") 96 | "Path to icon to use for difficulty level medium exercises." 97 | :type 'file 98 | :group 'exercism-modern) 99 | 100 | (defcustom exercism-modern-hard-icon (exercism-modern--load-from-package-root "icons/hard.svg") 101 | "Path to icon to use for difficulty leve hard exercises." 102 | :type 'file 103 | :group 'exercism-modern) 104 | 105 | (defcustom exercism-modern-success-icon (exercism-modern--load-from-package-root "icons/true.svg") 106 | "Path to icon to use for a success or true indication." 107 | :type 'file 108 | :group 'exercism-modern) 109 | 110 | (defcustom exercism-modern-failure-icon (exercism-modern--load-from-package-root "icons/false.svg") 111 | "Path to icon to use for a failure or false indication." 112 | :type 'file 113 | :group 'exercism-modern) 114 | 115 | (defvar exercism-modern-track-mode-map 116 | (let ((map (make-sparse-keymap))) 117 | (define-key map (kbd "") #'exercism-modern-track-view-exercises) 118 | map) 119 | "Keymap for `exercism-modern-track-mode'.") 120 | 121 | (defvar exercism-modern-exercise-mode-map 122 | (let ((map (make-sparse-keymap))) 123 | (define-key map (kbd "") #'exercism-modern-download-exercise) 124 | map) 125 | "Keymap for `exercism-modern-exercise-mode'.") 126 | 127 | ;;;###autoload 128 | (defun exercism-modern-get-config (&optional file-path) 129 | "Return parsed JSON config. 130 | Optionally check FILE-PATH instead." 131 | (json-read-file (or file-path exercism-modern-config-file))) 132 | 133 | (defun exercism-modern--get-icon (slug) 134 | "Get an icon for SLUG." 135 | (let ((path (expand-file-name (format "icons/%s.svg" slug) exercism-modern-cache-dir))) 136 | (unless (file-exists-p path) 137 | (mkdir (file-name-directory path) t) 138 | (let ((url (cdr (assoc slug exercism-modern--icon-urls)))) 139 | (request 140 | url 141 | :parser #'buffer-string 142 | :success (cl-function 143 | (lambda (&key data &allow-other-keys) 144 | (with-temp-buffer 145 | (insert data) 146 | (write-region (point-min) (point-max) path)))) 147 | :status-code `((403 . (lambda (&rest _) 148 | (unless (file-exists-p (expand-file-name "icons/_missing.svg" exercism-modern-cache-dir)) 149 | (url-copy-file exercism-modern-missing-icon (expand-file-name "icons/_missing.svg" exercism-modern-cache-dir))) 150 | (copy-file (expand-file-name "icons/_missing.svg" exercism-modern-cache-dir) ,path))))))) 151 | path)) 152 | 153 | (defun exercism-modern--endpoint->url (endpoint) 154 | "Convert an ENDPOINT to a callable url." 155 | (url-encode-url (mapconcat 'identity 156 | `(,exercism-modern-api-url ,endpoint) 157 | "/"))) 158 | 159 | (defun exercism-modern-request (endpoint &optional method) 160 | "Send a request to ENDPOINT using METHOD. 161 | METHOD defaults to GET and must be a valid argument to `request'." 162 | (let (result) 163 | (request 164 | (exercism-modern--endpoint->url endpoint) 165 | :type (or method "GET") 166 | :parser (lambda () 167 | (let ((json-array-type 'list)) 168 | (json-read))) 169 | :headers `(("Authorization" . ,(concat "Bearer " (alist-get 'token (exercism-modern-get-config))))) 170 | :success (cl-function 171 | (lambda (&key data &allow-other-keys) 172 | (setq result data))) 173 | :sync t) 174 | result)) 175 | 176 | (defun exercism-modern-get-tracks () 177 | "Get a list of all tracks." 178 | (let ((tracks (exercism-modern-request "tracks"))) 179 | (thread-first (alist-get 'tracks tracks) 180 | (cl-sort (lambda (lhs rhs) 181 | (and (string< (alist-get 'slug lhs) (alist-get 'slug rhs)) 182 | (and (alist-get 'is_joined lhs) (alist-get 'is_joined rhs)))))))) 183 | 184 | (defun exercism-modern-get-exercises (language) 185 | "Get all exercises for a LANGUAGE slug." 186 | (alist-get 'exercises (exercism-modern-request (format "tracks/%s/exercises" language)))) 187 | 188 | (defun exercism-modern-download-exercise () 189 | "Download a given exercise." 190 | (interactive) 191 | (cl-loop 192 | for exercise in (mapcar #'car (tablist-get-marked-items)) 193 | do (async-start-process 194 | "exercism-modern-download" 195 | exercism-modern-command 196 | (lambda (proc) 197 | ;; TODO Handle errors 198 | (message "Exercise cloned")) 199 | "download" 200 | (format "--exercise=%s" exercise) 201 | (format "--track=%s" exercism-modern-current-track)))) 202 | 203 | ;;;###autoload 204 | (defun exercism-modern-jump () 205 | "Open the exercism workspace in Dired." 206 | (interactive) 207 | (dired (alist-get 'workspace (exercism-modern-get-config)))) 208 | 209 | ;;;###autoload 210 | (defun exercism-modern-submit (&optional buffer-prefix-arg) 211 | "Submit the current exercise. 212 | Uses exercism metadata to get the correct file for submission. 213 | Pass prefix BUFFER-PREFIX-ARG to prompt for a buffer instead." 214 | (interactive (when (and current-prefix-arg) 215 | (list (read-buffer "Buffer to submit: ")))) 216 | (let ((solutions (map-nested-elt 217 | (exercism-modern-get-config 218 | (expand-file-name ".exercism/config.json" (locate-dominating-file "." ".exercism"))) 219 | '(files solution)))) 220 | (async-start-process 221 | "exercism-modern-submit" 222 | exercism-modern-command 223 | (lambda (proc) 224 | ;; TODO Handle submission errors 225 | (message "Submitted")) 226 | "submit" (if buffer-prefix-arg (buffer-file-name buffer-prefix-arg) 227 | (mapconcat 'identity solutions " "))))) 228 | 229 | 230 | ;;;###autoload 231 | (defun exercism-modern-track-view-exercises () 232 | "Invoked from `exercism-modern-track-mode', load the exercises for a given track." 233 | (interactive) 234 | (when (eq major-mode 'exercism-modern-track-mode) 235 | (setq exercism-modern-current-track (tabulated-list-get-id))) 236 | (pop-to-buffer (format "*exercism-modern-%s*" exercism-modern-current-track) nil) 237 | (exercism-modern-exercise-mode)) 238 | 239 | ;;;###autoload 240 | (defun exercism-modern-view-tracks () 241 | "View a listing of all current exercism tracks." 242 | (interactive) 243 | (pop-to-buffer "*exercism-modern-tracks*" nil) 244 | (exercism-modern-track-mode)) 245 | 246 | (define-derived-mode exercism-modern-exercise-mode tablist-mode "exercism-modern-exercise-mode" 247 | "Major mode for viewing exercism exercises." 248 | (let* ((exercises (exercism-modern-get-exercises exercism-modern-current-track)) 249 | (title-width (+ 6 (cl-loop for exercise in exercises maximize (length (alist-get 'title exercise)))))) 250 | (setq tabulated-list-format (vector 251 | (list "" 2 nil) ; is-recommended column 252 | (list "Exercise" title-width t) 253 | (list "Difficulty" 12 nil) 254 | (list "Description" 0 nil)) 255 | tabulated-list-entries (cl-loop 256 | for exercise in exercises 257 | collect 258 | (progn 259 | (add-to-list 'exercism-modern--icon-urls (cons (format "%s/%s" exercism-modern-current-track (alist-get 'slug exercise)) (alist-get 'icon_url exercise))) 260 | (let* ((slug (alist-get 'slug exercise)) 261 | (icon (exercism-modern--get-icon (format "%s/%s" exercism-modern-current-track (alist-get 'slug exercise)))) 262 | (title (alist-get 'title exercise)) 263 | (blurb (alist-get 'blurb exercise)) 264 | (difficulty (alist-get 'difficulty exercise)) 265 | (is-unlocked (not (eq :json-false (alist-get 'is_unlocked exercise)))) 266 | (is-recommended (not (eq :json-false (alist-get 'is_recommended exercise)))) 267 | (text-face (if is-unlocked 'default 'shadow)) 268 | (difficulty-svg (symbol-value (intern (concat "exercism-modern-" difficulty "-icon"))))) 269 | (list slug 270 | (vector (if is-recommended 271 | (propertize 272 | " " 273 | 'display 274 | `(image 275 | :margin (2 . 2) 276 | :ascent center 277 | :width ,(font-get (face-attribute 'default :font) :size) 278 | :type ,(image-type exercism-modern-star-icon) 279 | :file ,exercism-modern-star-icon)) 280 | "") 281 | (concat 282 | (propertize 283 | " " 284 | 'display 285 | `(image 286 | :margin (2 . 2) 287 | :ascent center 288 | :width ,(font-get (face-attribute 'default :font) :size) 289 | :type ,(image-type (alist-get 'icon_url exercise)) 290 | :file ,icon)) 291 | (propertize title 'face text-face)) 292 | (propertize 293 | " " 294 | 'display 295 | `(image 296 | :margin (2 . 2) 297 | :ascent center 298 | :width ,(* 4 (font-get (face-attribute 'default :font) :size)) 299 | :type ,(image-type difficulty-svg) 300 | :file ,difficulty-svg)) 301 | (propertize blurb 'face text-face)))))) 302 | tabulated-list-padding 4) 303 | (tabulated-list-init-header) 304 | (use-local-map exercism-modern-exercise-mode-map) 305 | (tabulated-list-print t) 306 | (tablist-minor-mode))) 307 | 308 | (define-derived-mode exercism-modern-track-mode tabulated-list-mode "exercism-modern-track-mode" 309 | "Major mode for viewing exercism tracks." 310 | (let* ((tracks (exercism-modern-get-tracks))) 311 | (setq tabulated-list-format (vector (list "Title" (+ 6 (cl-loop for track in tracks maximize (length (alist-get 'title track)))) t) 312 | (list "Joined" 8 t) 313 | (list "Concepts" 8 nil) 314 | (list "Exercises" 10 nil) 315 | (list "Solutions" 8 nil)) 316 | tabulated-list-entries (cl-loop 317 | for track in tracks 318 | collect 319 | (progn 320 | (add-to-list 'exercism-modern--icon-urls (cons (alist-get 'slug track) (alist-get 'icon_url track))) 321 | (let* ((slug (alist-get 'slug track)) 322 | (icon (exercism-modern--get-icon slug)) 323 | (title (alist-get 'title track)) 324 | (num-concepts (alist-get 'num_concepts track)) 325 | (num-exercises (alist-get 'num_exercises track)) 326 | (is-joined (alist-get 'is_joined track)) 327 | (join-icon (if is-joined exercism-modern-success-icon exercism-modern-failure-icon)) 328 | (num-learnt-concepts (alist-get 'num_learnt_concepts track)) 329 | (num-completed-exercises (alist-get 'num_completed_exercises track)) 330 | (num-solutions (alist-get 'num_solutions track))) 331 | (list slug 332 | (vector (concat 333 | (propertize 334 | " " 335 | 'display 336 | `(image 337 | :margin (2 . 2) 338 | :ascent center 339 | :width ,(font-get (face-attribute 'default :font) :size) 340 | :type ,(image-type icon) 341 | :file ,icon)) 342 | title) 343 | (propertize 344 | " " 345 | 'display 346 | `(image 347 | :margin (2 . 2) 348 | :ascent center 349 | :width ,(font-get (face-attribute 'default :font) :size) 350 | :type ,(image-type join-icon) 351 | :file ,join-icon)) 352 | (concat 353 | (number-to-string (if (numberp num-learnt-concepts) num-learnt-concepts 0)) 354 | "/" 355 | (number-to-string (if (numberp num-concepts) num-concepts 0))) 356 | (concat 357 | (number-to-string (if (numberp num-completed-exercises) num-completed-exercises 0)) 358 | "/" 359 | (number-to-string (if (numberp num-exercises) num-exercises 0))) 360 | (number-to-string (if (numberp num-learnt-concepts) num-learnt-concepts 0)) 361 | (number-to-string (if (numberp num-solutions) num-solutions 0))))))) 362 | tabulated-list-padding 4) 363 | (tabulated-list-init-header) 364 | (use-local-map exercism-modern-track-mode-map) 365 | (tabulated-list-print t))) 366 | 367 | (provide 'exercism-modern) 368 | 369 | ;;; exercism-modern.el ends here 370 | -------------------------------------------------------------------------------- /icons/easy.svg: -------------------------------------------------------------------------------- 1 | Easy 2 | -------------------------------------------------------------------------------- /icons/false.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /icons/hard.svg: -------------------------------------------------------------------------------- 1 | Hard 2 | -------------------------------------------------------------------------------- /icons/medium.svg: -------------------------------------------------------------------------------- 1 | Medium 2 | -------------------------------------------------------------------------------- /icons/star.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /icons/true.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/exercism-modern-test.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (describe "Exercism Tests" 4 | (describe "Config" 5 | (it "should get the workspace directory" 6 | (expect (downcase (alist-get 'workspace (exercism-modern-get-config))) :to-equal (expand-file-name "~/exercism")))) 7 | 8 | (describe "Icons" 9 | (before-each 10 | (setq exercism-modern-cache-dir "/tmp/exercism-test" 11 | exercism-modern--icon-urls '(("i_appear_missing" . "https://httpbin.org/status/403")) 12 | exercism-modern-missing-icon "https://httpbin.org/uuid") 13 | (when (file-exists-p exercism-modern-cache-dir) 14 | (delete-directory exercism-modern-cache-dir t t))))) 15 | -------------------------------------------------------------------------------- /test/test-helper.el: -------------------------------------------------------------------------------- 1 | ;;; test-helper.el --- Helper for tests 2 | ;; 3 | ;; Copyright (C) 2022 Ellis Kenyő 4 | ;; 5 | ;; This program is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation, either version 3 of the License, or 8 | ;; (at your option) any later version. 9 | 10 | ;; This program is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see . 17 | ;; 18 | ;;; Commentary: 19 | ;; 20 | ;; Test helpers 21 | ;; 22 | ;;; Code: 23 | 24 | (require 'f) 25 | (require 'buttercup) 26 | 27 | (when (require 'undercover nil t) 28 | (undercover "*.el" 29 | (:exclude "*-test.el"))) 30 | 31 | (require 'exercism-modern (f-expand "exercism-modern.el" (f-parent (f-parent (f-this-file))))) 32 | 33 | (provide 'test-helper) 34 | ;;; test-helper.el ends here 35 | --------------------------------------------------------------------------------