mirror of https://github.com/01-edu/public.git
fix docker image. update tester to work with docker image remove and solutions
This commit is contained in:
parent
b1e775a1ab
commit
893583e2af
|
@ -1,8 +1,10 @@
|
|||
FROM buildkite/puppeteer:latest
|
||||
|
||||
ENV GIT_TERMINAL_PROMPT=0
|
||||
# RUN apk add --no-cache git
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y git
|
||||
WORKDIR /app
|
||||
COPY ./puppeteer .
|
||||
COPY ./dom .
|
||||
COPY ./subjects ./subjects
|
||||
RUN ls -la
|
||||
ENTRYPOINT ["/bin/sh", "/app/entrypoint.sh"]
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
const body = document.querySelector('body')
|
||||
|
||||
const create = (tag) => {
|
||||
const element = document.createElement(tag)
|
||||
return element
|
||||
}
|
||||
|
||||
export const build = (amount = 54) => {
|
||||
let count = 1
|
||||
const intervalID = setInterval(() => {
|
||||
const brick = create('div')
|
||||
brick.title = 'brick'
|
||||
brick.id = `brick-${count}`
|
||||
if (count % 3 === 2) {
|
||||
brick.dataset.foundation = true
|
||||
}
|
||||
brick.append(count)
|
||||
body.append(brick)
|
||||
|
||||
if (count === amount) {
|
||||
window.clearInterval(intervalID)
|
||||
return
|
||||
}
|
||||
|
||||
count++
|
||||
}, 100)
|
||||
}
|
||||
|
||||
export const repair = (...ids) => {
|
||||
ids.forEach((id) => {
|
||||
const toRepair = document.getElementById(id)
|
||||
if (toRepair) {
|
||||
toRepair.dataset.repaired = toRepair.hasAttribute('data-foundation')
|
||||
? 'in progress'
|
||||
: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const destroy = () => {
|
||||
const bricks = [...document.querySelectorAll('[title="brick"]')]
|
||||
const toRemove = bricks[bricks.length - 1]
|
||||
|
||||
if (toRemove) {
|
||||
toRemove.remove()
|
||||
}
|
||||
}
|
|
@ -18,6 +18,5 @@ else
|
|||
cat > "$first_file"
|
||||
fi
|
||||
|
||||
cd
|
||||
|
||||
node --unhandled-rejections=strict /app/test.js "${EXERCISE}"
|
||||
cd /app
|
||||
node --no-warnings --unhandled-rejections=strict test.js "${EXERCISE}"
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import { colors } from './data.js'
|
||||
|
||||
export const generateClasses = () => {
|
||||
document.head.append(
|
||||
Object.assign(document.createElement('style'), {
|
||||
type: 'text/css',
|
||||
id: 'colors',
|
||||
innerHTML: colors
|
||||
.map((color) => `.${color} { background: ${color}; }`)
|
||||
.join('\n'),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const body = document.querySelector('body')
|
||||
|
||||
const cold = ['aqua', 'blue', 'turquoise', 'green', 'purple', 'cyan', 'navy']
|
||||
|
||||
export const generateColdShades = () => {
|
||||
const shades = colors.filter((color) => {
|
||||
for (const c of cold) {
|
||||
if (color.includes(c)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
shades.forEach((c) => {
|
||||
const shade = document.createElement('div')
|
||||
shade.className = c
|
||||
shade.textContent = c
|
||||
body.append(shade)
|
||||
})
|
||||
}
|
||||
|
||||
export const choseShade = (shade) => {
|
||||
const all = [...document.querySelectorAll('div')]
|
||||
all.forEach((a) => {
|
||||
if (!a.classList.contains(shade)) {
|
||||
a.classList.replace(a.className, shade)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
export const getArchitects = () => {
|
||||
const architects = [...document.getElementsByTagName('a')]
|
||||
const others = [...document.getElementsByTagName('span')]
|
||||
return [architects, others]
|
||||
}
|
||||
|
||||
export const getClassical = () => {
|
||||
const classicals = [...document.getElementsByClassName('classical')]
|
||||
const others = [...document.querySelectorAll('a:not(.classical)')]
|
||||
return [classicals, others]
|
||||
}
|
||||
|
||||
export const getActive = () => {
|
||||
const active = [...document.querySelectorAll('.classical.active')]
|
||||
const others = [...document.querySelectorAll('.classical:not(.active)')]
|
||||
return [active, others]
|
||||
}
|
||||
|
||||
export const getBonannoPisano = () => {
|
||||
const bonanno = document.getElementById('BonannoPisano')
|
||||
const others = [
|
||||
...document.querySelectorAll('a.classical.active:not(#BonannoPisano)'),
|
||||
]
|
||||
return [bonanno, others]
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
import { gossips } from './data.js'
|
||||
|
||||
const body = document.querySelector('body')
|
||||
|
||||
const ranges = document.createElement('div')
|
||||
ranges.className = 'ranges'
|
||||
body.append(ranges)
|
||||
|
||||
const inputs = [
|
||||
{ props: ['width'], min: 200, max: 800, value: 250 },
|
||||
{ props: ['fontSize', 'lineHeight'], min: 20, max: 40, value: 25 },
|
||||
{ props: ['background'], min: 20, max: 75, value: 60 },
|
||||
]
|
||||
|
||||
export const grid = () => {
|
||||
inputs.forEach((input) => createInput(input))
|
||||
createAddGossip()
|
||||
gossips.forEach((g) => createGossip(g))
|
||||
}
|
||||
|
||||
const createGossip = (g, isNew = false) => {
|
||||
const gossip = document.createElement('div')
|
||||
const addGossip = document.getElementById('add-gossip')
|
||||
const { fontSize, lineHeight, width, background } = addGossip.style
|
||||
gossip.className = 'gossip'
|
||||
gossip.textContent = g
|
||||
if (isNew) {
|
||||
gossip.style.fontSize = fontSize
|
||||
gossip.style.lineHeight = lineHeight
|
||||
gossip.style.width = width
|
||||
gossip.style.background = background
|
||||
gossip.classList.add('fade-in')
|
||||
body.insertBefore(gossip, addGossip.nextElementSibling)
|
||||
} else {
|
||||
body.append(gossip)
|
||||
}
|
||||
}
|
||||
|
||||
const createAddGossip = () => {
|
||||
const addGossip = document.createElement('form')
|
||||
addGossip.className = 'gossip'
|
||||
addGossip.id = 'add-gossip'
|
||||
addGossip.onsubmit = () => false
|
||||
|
||||
const newInput = document.createElement('textarea')
|
||||
newInput.autofocus = true
|
||||
newInput.placeholder = 'Got a gossip to share ?'
|
||||
newInput.addEventListener('keyup', (e) => addNewGossip(newInput, e))
|
||||
|
||||
const button = document.createElement('button')
|
||||
button.className = 'button'
|
||||
button.textContent = 'Share gossip!'
|
||||
button.addEventListener('click', (e) => addNewGossip(newInput))
|
||||
|
||||
addGossip.append(newInput, button)
|
||||
body.append(addGossip)
|
||||
}
|
||||
|
||||
const addNewGossip = (input, event) => {
|
||||
const noValue = !input.value.trim()
|
||||
const notEnterKey = event && event.keyCode !== 13
|
||||
if (notEnterKey || noValue) return
|
||||
createGossip(input.value, true)
|
||||
gossips.push(input.value)
|
||||
input.value = ''
|
||||
input.focus()
|
||||
}
|
||||
|
||||
const createInput = ({ props, min, max, value }) => {
|
||||
const range = document.createElement('div')
|
||||
range.className = 'range'
|
||||
|
||||
const input = document.createElement('input')
|
||||
input.type = 'range'
|
||||
input.min = min
|
||||
input.max = max
|
||||
input.value = value
|
||||
input.addEventListener('input', (e) => customize(e, ...props))
|
||||
|
||||
const propLabel = document.createElement('label')
|
||||
propLabel.textContent = props[0]
|
||||
|
||||
const valueLabel = document.createElement('span')
|
||||
valueLabel.textContent = value
|
||||
|
||||
range.append(propLabel, input, valueLabel)
|
||||
ranges.append(range)
|
||||
}
|
||||
|
||||
const customize = ({ target }, ...props) => {
|
||||
for (const card of [...document.querySelectorAll('.gossip')]) {
|
||||
for (const prop of props) {
|
||||
const updatedValue =
|
||||
(prop === 'lineHeight' && `${Number(target.value) * 1.5}px`) ||
|
||||
(prop === 'background' && `hsl(280, 50%, ${target.value}%)`) ||
|
||||
`${target.value}px`
|
||||
card.style[prop] = updatedValue
|
||||
}
|
||||
}
|
||||
|
||||
const valueLabel = target.nextElementSibling
|
||||
valueLabel.textContent = target.value
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
const body = document.querySelector('body')
|
||||
|
||||
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
|
||||
export const generateLetters = () => {
|
||||
body.append(...[...Array(120).keys()].map((c) => {
|
||||
const shape = document.createElement('div')
|
||||
|
||||
shape.textContent = alphabet[Math.floor(Math.random() * alphabet.length)]
|
||||
shape.style.fontSize = `${c + 11}px`
|
||||
shape.style.fontWeight = [300,400,600][Math.floor(c / 40)]
|
||||
return shape
|
||||
}))
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
const body = document.querySelector('body')
|
||||
|
||||
export const compose = () => {
|
||||
document.addEventListener('keydown', (e) => handleKey(e))
|
||||
setTimeout(
|
||||
() => document.removeEventListener('keydown', (e) => handleKey(e)),
|
||||
500,
|
||||
)
|
||||
}
|
||||
|
||||
const handleKey = (e) => {
|
||||
const notes = [...document.querySelectorAll('.note')]
|
||||
|
||||
if (e.key === 'Backspace') {
|
||||
const last = notes[notes.length - 1]
|
||||
last && last.remove()
|
||||
return
|
||||
}
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
if (notes.length) {
|
||||
notes.forEach((note) => note.remove())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
createNote(e)
|
||||
}
|
||||
|
||||
const createNote = ({ key }) => {
|
||||
const number = key.charCodeAt(0) * 2 - 150
|
||||
const note = document.createElement('div')
|
||||
note.className = 'note'
|
||||
note.textContent = key
|
||||
note.style.background = `hsl(270, ${number}%, ${number}%)`
|
||||
body.append(note)
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
const body = document.querySelector('body')
|
||||
|
||||
const box = document.createElement('div')
|
||||
box.className = 'box'
|
||||
body.append(box)
|
||||
|
||||
const { top, bottom, left, right } = box.getBoundingClientRect()
|
||||
|
||||
const diameter = 50
|
||||
const radius = diameter / 2
|
||||
|
||||
const insideX = (clientX) => clientX > left + radius && clientX < right - radius
|
||||
const insideY = (clientY) => clientY > top + radius && clientY < bottom - radius
|
||||
|
||||
let isInside = false
|
||||
|
||||
export const createCircle = () => {
|
||||
document.addEventListener('click', (e) => create(e))
|
||||
setTimeout(() => document.removeEventListener('click', create), 500)
|
||||
}
|
||||
|
||||
const create = ({ clientX, clientY }) => {
|
||||
const circle = document.createElement('div')
|
||||
circle.className = 'circle'
|
||||
body.append(circle)
|
||||
circle.style.top = `${clientY - radius}px`
|
||||
circle.style.left = `${clientX - radius}px`
|
||||
circle.style.background = 'white'
|
||||
const hasEntered = insideX(clientX) && insideY(clientY)
|
||||
if (hasEntered) {
|
||||
circle.style.background = 'var(--purple)'
|
||||
}
|
||||
isInside = false
|
||||
}
|
||||
|
||||
export const moveCircle = () => {
|
||||
document.addEventListener('mousemove', (e) => move(e))
|
||||
setTimeout(() => document.removeEventListener('mousemove', move), 500)
|
||||
}
|
||||
|
||||
const move = (e) => {
|
||||
const circles = [...document.getElementsByClassName('circle')]
|
||||
const circle = circles[circles.length - 1]
|
||||
if (!circle) return
|
||||
position(e, circle)
|
||||
}
|
||||
|
||||
const position = ({ clientX, clientY }, circle) => {
|
||||
const hasEntered = insideX(clientX) && insideY(clientY)
|
||||
|
||||
if (hasEntered) {
|
||||
isInside = true
|
||||
circle.style.background = 'var(--purple)'
|
||||
}
|
||||
|
||||
if (isInside) {
|
||||
if (insideY(clientY)) {
|
||||
circle.style.top = `${clientY - radius}px`
|
||||
}
|
||||
if (insideX(clientX)) {
|
||||
circle.style.left = `${clientX - radius}px`
|
||||
}
|
||||
} else {
|
||||
circle.style.top = `${clientY - radius}px`
|
||||
circle.style.left = `${clientX - radius}px`
|
||||
}
|
||||
}
|
|
@ -1,9 +1,3 @@
|
|||
{
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "node --unhandled-rejections=strict test"
|
||||
},
|
||||
"dependencies": {
|
||||
"puppeteer-core": "^3.3.0"
|
||||
}
|
||||
"type": "module"
|
||||
}
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
const body = document.querySelector('body')
|
||||
|
||||
const hslValue = document.createElement('div')
|
||||
hslValue.className = 'hsl'
|
||||
hslValue.textContent = 'hsl(0, 50%, 0%)'
|
||||
|
||||
const hueValue = document.createElement('div')
|
||||
hueValue.className = 'text hue'
|
||||
hueValue.textContent = 'hue'
|
||||
|
||||
const luminosityValue = document.createElement('div')
|
||||
luminosityValue.className = 'text luminosity'
|
||||
luminosityValue.textContent = 'luminosity'
|
||||
|
||||
const origin = document.createElement('div')
|
||||
origin.className = 'text origin'
|
||||
|
||||
const picked = document.createElement('div')
|
||||
picked.className = 'text picked'
|
||||
picked.textContent = 'Color successfully picked!'
|
||||
|
||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
||||
svg.setAttributeNS(
|
||||
'http://www.w3.org/2000/xmlns/',
|
||||
'xmlns:xlink',
|
||||
'http://www.w3.org/1999/xlink',
|
||||
)
|
||||
svg.setAttribute('width', window.innerWidth)
|
||||
svg.setAttribute('height', window.innerHeight)
|
||||
svg.setAttribute('viewBox', `0 0 ${window.innerWidth} ${window.innerHeight}`)
|
||||
|
||||
const axisX = document.createElementNS('http://www.w3.org/2000/svg', 'line')
|
||||
axisX.setAttribute('y1', window.innerHeight)
|
||||
axisX.setAttribute('y2', 0)
|
||||
axisX.id = 'axisX'
|
||||
svg.append(axisX)
|
||||
|
||||
const axisY = document.createElementNS('http://www.w3.org/2000/svg', 'line')
|
||||
axisY.setAttribute('x1', window.innerWidth)
|
||||
axisY.setAttribute('x2', 0)
|
||||
axisY.id = 'axisY'
|
||||
svg.append(axisY)
|
||||
|
||||
body.append(hslValue, hueValue, luminosityValue, origin, picked, svg)
|
||||
|
||||
export const pick = () => {
|
||||
document.addEventListener('mousemove', (e) => set(e))
|
||||
setTimeout(
|
||||
() => document.removeEventListener('mousemove', (e) => set(e)),
|
||||
500,
|
||||
)
|
||||
|
||||
body.addEventListener('click', click)
|
||||
setTimeout(() => document.removeEventListener('click', click), 500)
|
||||
|
||||
body.addEventListener('copy', copy)
|
||||
setTimeout(() => document.removeEventListener('copy', copy), 500)
|
||||
}
|
||||
|
||||
const click = (e) => {
|
||||
document.execCommand('copy')
|
||||
const wave = document.createElement('div')
|
||||
wave.className = 'click-wave'
|
||||
wave.style.top = `${e.clientY - 10}px`
|
||||
wave.style.left = `${e.clientX - 10}px`
|
||||
body.append(wave)
|
||||
setTimeout(() => wave.remove(), 150)
|
||||
}
|
||||
|
||||
const copy = (event) => {
|
||||
event.preventDefault()
|
||||
if (event.clipboardData) {
|
||||
event.clipboardData.setData('text/plain', hslValue.textContent)
|
||||
picked.classList.add('fade-in')
|
||||
setTimeout(() => picked.classList.remove('fade-in'), 1000)
|
||||
}
|
||||
}
|
||||
|
||||
const calc = (number, max) =>
|
||||
Math.round(Math.min(max, Math.max(0, max * number)))
|
||||
|
||||
const set = ({ clientX, clientY }) => {
|
||||
const { innerWidth, innerHeight } = window
|
||||
const padding = 100
|
||||
const mouseX = clientX - padding
|
||||
const mouseY = clientY - padding
|
||||
|
||||
const hue = calc(mouseX / (innerWidth - padding * 2), 360)
|
||||
const luminosity = calc(mouseY / (innerHeight - padding * 2), 100)
|
||||
const color = `hsl(${hue}, 50%, ${luminosity}%)`
|
||||
|
||||
axisX.setAttribute('x1', clientX)
|
||||
axisX.setAttribute('x2', clientX)
|
||||
axisY.setAttribute('y1', clientY)
|
||||
axisY.setAttribute('y2', clientY)
|
||||
|
||||
axisX.setAttribute('stroke', color)
|
||||
axisY.setAttribute('stroke', color)
|
||||
|
||||
body.style.color = color
|
||||
body.style.background = color
|
||||
origin.style.background = color
|
||||
hslValue.textContent = color
|
||||
|
||||
hueValue.textContent = `hue\n${hue}`
|
||||
luminosityValue.textContent = `${luminosity}\nluminosity`
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import { styles } from './data.js'
|
||||
|
||||
let count = 0
|
||||
let increment = 1
|
||||
|
||||
export const pimp = () => {
|
||||
const button = document.querySelector('.button')
|
||||
|
||||
const ceiling = count === styles.length - 1
|
||||
const floor = !count
|
||||
|
||||
const increasing = increment > 0
|
||||
|
||||
if (increasing || (floor && !increment)) {
|
||||
button.classList.add(styles[count])
|
||||
} else {
|
||||
button.classList.remove(styles[count])
|
||||
}
|
||||
|
||||
if (ceiling) {
|
||||
increment = increment ? 0 : -1
|
||||
}
|
||||
if (floor) {
|
||||
increment = increment < 0 ? 0 : 1
|
||||
}
|
||||
|
||||
button.classList.toggle('unpimp', increment < 0 || (!increment && ceiling))
|
||||
|
||||
count += increment
|
||||
}
|
14
dom/test.js
14
dom/test.js
|
@ -2,14 +2,20 @@ import http from 'http'
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { deepStrictEqual } from 'assert'
|
||||
import puppeteer from 'puppeteer-core'
|
||||
import puppeteer from 'puppeteer'
|
||||
|
||||
const exercise = process.argv[2]
|
||||
if (!exercise) throw Error(`usage: node test EXERCISE_NAME`)
|
||||
const PORT = 9898
|
||||
const config = {
|
||||
headless: false,
|
||||
executablePath: process.env.CHROME_PATH || '/usr/bin/google-chrome',
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
|
||||
// This will write shared memory files into /tmp instead of /dev/shm,
|
||||
// because Docker’s default for /dev/shm is 64MB
|
||||
'--disable-dev-shm-usage',
|
||||
],
|
||||
}
|
||||
|
||||
const mediaTypes = {
|
||||
|
@ -25,7 +31,7 @@ const server = http
|
|||
.createServer(({ url, method }, response) => {
|
||||
console.log(method + ' ' + url)
|
||||
const filepath = url.endsWith(`${exercise}/${exercise}.js`)
|
||||
? path.join('.', url.slice(exercise.length + 1))
|
||||
? path.join('/jail/student', url.slice(exercise.length + 1))
|
||||
: path.join('./subjects', url)
|
||||
const ext = path.extname(filepath)
|
||||
response.setHeader('Content-Type', mediaTypes[ext.slice(1)] || 'text/plain')
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
import { places } from './data.js'
|
||||
|
||||
const body = document.querySelector('body')
|
||||
|
||||
export const explore = () => {
|
||||
createSections()
|
||||
|
||||
const location = document.createElement('div')
|
||||
location.className = 'location'
|
||||
setLocation(location)
|
||||
|
||||
const direction = document.createElement('div')
|
||||
direction.className = 'direction'
|
||||
|
||||
body.append(location, direction)
|
||||
|
||||
document.addEventListener('wheel', (event) =>
|
||||
setLocation(location, direction, event),
|
||||
)
|
||||
setTimeout(() =>
|
||||
document.removeEventListener('wheel', (event) =>
|
||||
setLocation(location, direction, event),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const createSections = () => {
|
||||
const sorted = places.sort(
|
||||
(a, b) => getDegree(b.coordinates) - getDegree(a.coordinates),
|
||||
)
|
||||
|
||||
sorted.map(({ name, color }) => {
|
||||
const nameDashCase = name
|
||||
.toLowerCase()
|
||||
.split(',')[0]
|
||||
.split(' ')
|
||||
.join('-')
|
||||
|
||||
const url = `https://raw.githubusercontent.com/MarieMalarme/dom-js/master/assets/images/${nameDashCase}.jpg`
|
||||
|
||||
const section = document.createElement('section')
|
||||
section.style.background = `center / cover url(${url})`
|
||||
body.append(section)
|
||||
})
|
||||
}
|
||||
|
||||
const getDegree = (coordinates) => {
|
||||
const north = coordinates.includes('N')
|
||||
const degree = coordinates.split("'")[0].replace('°', '.')
|
||||
return north ? degree : -degree
|
||||
}
|
||||
|
||||
const setLocation = (location, direction, event) => {
|
||||
const { name, coordinates, color } = getLocation()
|
||||
location.textContent = `${name}\n${coordinates}`
|
||||
location.style.color = color
|
||||
location.onclick = () => {
|
||||
window.open(`https://www.google.com/maps/place/${coordinates}`, '_blank')
|
||||
}
|
||||
|
||||
if (!event) return
|
||||
const scrollUp = event.deltaY < 0
|
||||
direction.innerHTML = `<div style="transform: rotate(${
|
||||
scrollUp ? -90 : 90
|
||||
}deg)">➢</div><div>${scrollUp ? 'N' : 'S'}</div>`
|
||||
}
|
||||
|
||||
const getLocation = () => {
|
||||
const { innerHeight, scrollY } = window
|
||||
const index = Math.ceil((scrollY - innerHeight / 2) / innerHeight)
|
||||
return places[index]
|
||||
}
|
Loading…
Reference in New Issue