Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion .github/workflows/screenshot-comparison.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ jobs:
check-screenshot:
name: Check Screenshot
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # needed to post the comment on screenshot mismatch
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down Expand Up @@ -41,10 +44,41 @@ jobs:
pip install --upgrade Pillow

- uses: nanasess/setup-chromedriver@master
- run: |
- name: Run screenshot test
run: |
export DISPLAY=:99
chromedriver --url-base=/wd/hub &
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional
python3 $GITHUB_WORKSPACE/test/test.py
python3 $GITHUB_WORKSPACE/test/test_figures.py

- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v4
with:
name: screenshot-comparison
path: |
widget-01.png
comparison.png
test/widget-sample.png
if-no-files-found: ignore

- name: Comment on PR with refresh instructions
if: failure() && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const url = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: [
`📷 **Screenshot comparison failed.**`,
``,
`Download the **screenshot-comparison** artifact from [this run](${url}) and open \`comparison.png\` — it shows **reference | new | diff** side by side.`,
``,
`If the change is acceptable, replace \`test/widget-sample.png\` with the new \`widget-01.png\` from the artifact and push the update.`,
].join('\n'),
});

24 changes: 21 additions & 3 deletions js/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ function generateRandomString(length) {
return result.substring(0, length);
}

// Stop keydown events from bubbling out of the editor's DOM. Without this,
// when the widget is hosted inside a JupyterLab notebook output area,
// global keybindings like Shift+L are caught by JupyterLab's command-mode
// keybindings on the surrounding cell — Lumino calls preventDefault(), which
// in turn suppresses the subsequent `input` event that CodeMirror 6 relies on
// to insert characters, so the keystroke silently disappears.
// stopPropagation() only blocks ancestors; CodeMirror's own handlers on the
// same element still run, so editor behavior is unchanged.
const stopKeydownPropagation = EditorView.domEventHandlers({
keydown: (event) => {
event.stopPropagation();
return false;
}
});


export default{
initialize({ model }) {
Expand Down Expand Up @@ -133,7 +148,8 @@ export default{
var myDocstringCodeMirror;

const mySignatureCodeMirror = editorFromTextArea(document.getElementById(theTextareaId + '-signature'), [EditorState.readOnly.of(true),lineNumbers(),indentUnit.of(" ")
,
,
stopKeydownPropagation,
history(),
drawSelection(),
dropCursor(),
Expand Down Expand Up @@ -165,7 +181,7 @@ export default{
if (model.get('docstring') !== '') {

myDocstringCodeMirror = editorFromTextArea(document.getElementById(theTextareaId + '-docstring'), [lineNumbers(), EditorState.readOnly.of(true), EditorView.editorAttributes.of({class:"widget-code-input-docstring"}), gutter({class:"forced-indent"}), guttercomp.of(lineNumbers()),
stopKeydownPropagation,
history(),
drawSelection(),
dropCursor(),
Expand Down Expand Up @@ -204,7 +220,8 @@ export default{



var myBodyCodeMirror = editorFromTextArea(document.getElementById(theTextareaId + '-body'), [guttercomp.of(lineNumbers()),gutter({class:"forced-indent"}), bodyUpdateListenerCompartment.of([]),
var myBodyCodeMirror = editorFromTextArea(document.getElementById(theTextareaId + '-body'), [guttercomp.of(lineNumbers()),gutter({class:"forced-indent"}), bodyUpdateListenerCompartment.of([]),
stopKeydownPropagation,
history(),
drawSelection(),
dropCursor(),
Expand Down Expand Up @@ -363,6 +380,7 @@ function signatureValueChanged() {
EditorState.readOnly.of(true),
EditorView.editorAttributes.of({ class: 'widget-code-input-docstring' }),
gutter({ class: 'forced-indent' }),guttercomp.of(lineNumbers()),
stopKeydownPropagation,
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
python(),
indentUnit.of(' '),
Expand Down
13 changes: 0 additions & 13 deletions test/test.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
# Generated by Selenium IDE
import pytest
import time
import json
import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

class test_widget():
def setup_method(self, method):
Expand All @@ -30,11 +22,6 @@ def download_widget_image(self):
time.sleep(3)
self.driver.save_screenshot("widget-01.png")

self.driver.find_element(By.CLASS_NAME, 'widget-code-input-signature').click()
self.driver.find_element(By.TAG_NAME, 'body').send_keys(Keys.END)
time.sleep(3)
self.driver.save_screenshot("widget-02.png")

test = test_widget()
test.setup_method('Chrome')
test.download_widget_image()
Expand Down
20 changes: 15 additions & 5 deletions test/test_figures.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
from PIL import Image, ImageChops, ImageStat

image1 = Image.open('widget-01.png')
image2 = Image.open('test/widget-sample.png')
new = Image.open('widget-01.png')
reference = Image.open('test/widget-sample.png')

diff = ImageChops.difference(image1, image2)
diff = ImageChops.difference(new, reference)
stat = ImageStat.Stat(diff)

if sum(stat.mean) == 0:
print('images are the same')
print('images are the same')
else:
raise Exception("The result is NOT the same as expected. Please check matplotlib version.")
w = max(new.width, reference.width, diff.width)
h = max(new.height, reference.height, diff.height)
composite = Image.new('RGB', (w * 3, h), 'white')
composite.paste(reference, (0, 0)) # left: reference
composite.paste(new, (w, 0)) # middle: new from this run
composite.paste(diff, (w * 2, 0)) # right: pixel-wise diff
composite.save('comparison.png')
print(f"Screenshots differ. Mean per-channel diff: {stat.mean}")
print("Inspect the 'screenshot-comparison' workflow artifact.")
print("If the change is acceptable, replace test/widget-sample.png with widget-01.png and commit.")
raise SystemExit(1)
Binary file modified test/widget-sample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading