Merge remote-tracking branch 'penpot/develop' into token-studio-develop

This commit is contained in:
Florian Schroedl
2024-08-06 11:06:51 +02:00
710 changed files with 33332 additions and 24717 deletions

1
frontend/.gitignore vendored
View File

@@ -10,3 +10,4 @@ node_modules/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/**/visual-specs/**/*.png

View File

@@ -1,2 +0,0 @@
---
printWidth: 110

11
frontend/.prettierrc.json Normal file
View File

@@ -0,0 +1,11 @@
{
"overrides": [
{
"files": "*.scss",
"options": {
"printWidth": 110
}
}
]
}

View File

@@ -2,18 +2,17 @@
const config = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
staticDirs: ["../resources/public"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@storybook/addon-interactions",
],
addons: ["@storybook/addon-essentials", "@storybook/addon-themes"],
core: {
builder: "@storybook/builder-vite",
options: {
viteConfigPath: "../vite.config.js",
},
},
framework: {
name: "@storybook/react-vite",
options: {},
},
docs: {
autodocs: "tag",
},
docs: {},
};
export default config;

View File

@@ -1 +0,0 @@
<link href="/css/main.css" rel="stylesheet" type="text/css" />

View File

@@ -1,15 +1,27 @@
import "../resources/public/css/main.css";
import { withThemeByClassName } from "@storybook/addon-themes";
export const decorators = [
withThemeByClassName({
themes: {
light: "light",
dark: "default",
},
defaultTheme: "dark",
parentSelector: "body",
}),
];
/** @type { import('@storybook/react').Preview } */
const preview = {
decorators: decorators,
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
backgrounds: { disable: true },
},
};

View File

@@ -3,27 +3,28 @@
{penpot/common
{:local/root "../common"}
org.clojure/clojure {:mvn/version "1.11.1"}
org.clojure/clojure {:mvn/version "1.11.3"}
binaryage/devtools {:mvn/version "RELEASE"}
metosin/reitit-core {:mvn/version "0.6.0"}
metosin/reitit-core {:mvn/version "0.7.0"}
funcool/okulary {:mvn/version "2022.04.11-16"}
funcool/potok2
{:git/tag "v2.1"
:git/sha "84c97b9"
:git/url "https://github.com/funcool/potok.git"}
:git/url "https://github.com/funcool/potok.git"
:exclusions [funcool/beicon2]}
funcool/beicon2
{:git/tag "v2.0"
:git/sha "e7135e0"
{:git/tag "v2.1"
:git/sha "7d648e1"
:git/url "https://github.com/funcool/beicon.git"}
funcool/rumext
{:git/tag "v2.11.3"
:git/sha "b1f6ce4"
{:git/tag "v2.12"
:git/sha "ab819f5"
:git/url "https://github.com/funcool/rumext.git"}
instaparse/instaparse {:mvn/version "1.4.12"}
instaparse/instaparse {:mvn/version "1.5.0"}
garden/garden {:git/url "https://github.com/noprompt/garden"
:git/sha "05590ecb5f6fa670856f3d1ab400aa4961047480"}
}
@@ -41,12 +42,11 @@
:dev
{:extra-paths ["dev"]
:extra-deps
{thheller/shadow-cljs {:mvn/version "2.27.4"}
{thheller/shadow-cljs {:mvn/version "2.28.11"}
org.clojure/tools.namespace {:mvn/version "RELEASE"}
cider/cider-nrepl {:mvn/version "0.44.0"}}}
cider/cider-nrepl {:mvn/version "0.48.0"}}}
:shadow-cljs
{:main-opts ["-m" "shadow.cljs.devtools.cli"]}
}}

View File

@@ -4,10 +4,8 @@
"license": "MPL-2.0",
"author": "Kaleidos INC",
"private": true,
"packageManager": "yarn@4.2.2",
"browserslist": [
"defaults"
],
"packageManager": "yarn@4.3.1",
"browserslist": ["defaults"],
"type": "module",
"repository": {
"type": "git",
@@ -17,48 +15,48 @@
"@vitejs/plugin-react": "^4.2.0"
},
"scripts": {
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
"build:app:assets": "node ./scripts/build-app-assets.js",
"build:storybook": "yarn run build:storybook:assets && yarn run build:storybook:cljs && storybook build",
"build:storybook:assets": "node ./scripts/build-storybook-assets.js",
"build:storybook:cljs": "clojure -M:dev:shadow-cljs release storybook",
"e2e:server": "node ./scripts/e2e-server.js",
"e2e:test": "playwright test --project default",
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
"fmt:js": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -w",
"fmt:js:check": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js",
"lint:clj": "clj-kondo --parallel --lint src/",
"lint:scss": "yarn run prettier -c resources/styles -c src/**/*.scss",
"lint:scss:fix": "yarn run prettier -c resources/styles -c src/**/*.scss -w",
"lint:clj": "clj-kondo --parallel --lint src/",
"test": "yarn run test:compile && yarn run test:run",
"test:compile": "clojure -M:dev:shadow-cljs compile test --config-merge '{:autorun false}'",
"test:run": "node target/tests.cjs",
"test:watch": "clojure -M:dev:shadow-cljs watch test",
"test": "yarn run test:compile && yarn run test:run",
"token-test:compile": "clojure -M:dev:shadow-cljs compile test-esm --config-merge '{:autorun false}'",
"token-test:run": "bun target/tests-esm.cjs",
"token-test:watch": "clojure -M:dev:shadow-cljs watch test-esm",
"token-test:nodemon": "nodemon --watch ./target/tests-esm.cjs --exec 'bun run token-test:run'",
"token-test": "yarn run token-test:compile && yarn run token-test:run",
"translations:validate": "node ./scripts/validate-translations.js",
"translations:find-unused": "node ./scripts/find-unused-translations.js",
"compile": "node ./scripts/compile.js",
"compile:cljs": "clojure -M:dev:shadow-cljs compile main",
"watch": "node ./scripts/watch.js",
"e2e:server": "node ./scripts/e2e-server.js",
"e2e:test": "playwright test --project default",
"storybook:compile": "yarn run compile && clojure -M:dev:shadow-cljs compile storybook",
"storybook:watch": "yarn run storybook:compile && concurrently \"clojure -M:dev:shadow-cljs watch storybook\" \"storybook dev -p 6006\" \"yarn run watch\"",
"storybook:build": "yarn run storybook:compile && storybook build"
"translations": "node ./scripts/translations.js",
"watch": "yarn run watch:app:assets",
"watch:app:assets": "node ./scripts/watch.js",
"watch:storybook": "concurrently \"clojure -M:dev:shadow-cljs watch storybook\" \"storybook dev -p 6006 --no-open\" \"yarn run watch:storybook:assets\"",
"watch:storybook:assets": "node ./scripts/watch-storybook.js"
},
"devDependencies": {
"@playwright/test": "1.42.1",
"@storybook/addon-essentials": "^7.6.17",
"@storybook/addon-interactions": "^7.6.17",
"@storybook/addon-links": "^7.6.17",
"@storybook/addon-onboarding": "^1.0.11",
"@storybook/blocks": "^7.6.17",
"@storybook/react": "^7.6.17",
"@storybook/react-vite": "^7.6.17",
"@storybook/testing-library": "^0.2.2",
"@playwright/test": "1.44.1",
"@storybook/addon-essentials": "^8.2.2",
"@storybook/addon-themes": "^8.2.2",
"@storybook/blocks": "^8.2.2",
"@storybook/react": "^8.2.2",
"@storybook/react-vite": "^8.2.2",
"@types/node": "^20.11.20",
"animate.css": "^4.1.1",
"autoprefixer": "^10.4.17",
"autoprefixer": "^10.4.19",
"concurrently": "^8.2.2",
"draft-js": "git+https://github.com/penpot/draft-js.git#commit=4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0",
"express": "^4.19.2",
"fancy-log": "^2.0.0",
"getopts": "^2.3.0",
"gettext-parser": "^8.0.0",
"gulp": "4.0.2",
"gulp-concat": "^2.6.1",
@@ -69,34 +67,35 @@
"gulp-sass": "^5.1.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-svg-sprite": "^2.0.3",
"jsdom": "^24.0.0",
"jsdom": "^24.1.0",
"map-stream": "0.0.7",
"marked": "^12.0.0",
"marked": "^12.0.2",
"mkdirp": "^3.0.1",
"mustache": "^4.2.0",
"nodemon": "^3.1.0",
"nodemon": "^3.1.2",
"npm-run-all": "^4.1.5",
"p-limit": "^5.0.0",
"postcss": "^8.4.35",
"postcss": "^8.4.38",
"postcss-clean": "^1.2.2",
"prettier": "^3.2.5",
"prettier": "3.3.2",
"pretty-time": "^1.1.0",
"prop-types": "^15.8.1",
"rimraf": "^5.0.5",
"sass": "^1.71.1",
"sass-embedded": "^1.71.1",
"shadow-cljs": "2.27.4",
"storybook": "^7.6.17",
"svg-sprite": "^2.0.2",
"typescript": "^5.3.3",
"rimraf": "^5.0.7",
"sass": "^1.77.4",
"sass-embedded": "^1.77.2",
"shadow-cljs": "2.28.11",
"storybook": "^8.2.2",
"svg-sprite": "^2.0.4",
"typescript": "^5.4.5",
"vite": "^5.1.4",
"vitest": "^1.3.1",
"watcher": "^2.3.0",
"workerpool": "^9.1.0"
"watcher": "^2.3.1",
"workerpool": "^9.1.1"
},
"dependencies": {
"@tokens-studio/sd-transforms": "^0.16.1",
"date-fns": "^3.3.1",
"compression": "^1.7.4",
"date-fns": "^3.6.0",
"eventsource-parser": "^1.1.2",
"highlight.js": "^11.9.0",
"js-beautify": "^1.15.1",
@@ -106,15 +105,15 @@
"opentype.js": "^1.3.4",
"postcss-modules": "^6.0.0",
"randomcolor": "^0.6.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-virtualized": "^9.22.5",
"rxjs": "8.0.0-alpha.14",
"sax": "^1.3.0",
"sax": "^1.4.1",
"source-map-support": "^0.5.21",
"style-dictionary": "patch:style-dictionary@npm%3A4.0.0-prerelease.36#~/.yarn/patches/style-dictionary-npm-4.0.0-prerelease.36-55c0fc33bd.patch",
"tdigest": "^0.1.2",
"ua-parser-js": "^1.0.37",
"ua-parser-js": "^1.0.38",
"xregexp": "^5.1.1"
}
}

View File

@@ -48,7 +48,7 @@ export default defineConfig({
use: { ...devices["Desktop Chrome"] },
testDir: "./playwright/ui/visual-specs",
expect: {
toHaveScreenshot: { maxDiffPixelRatio: 0.01 },
toHaveScreenshot: { maxDiffPixelRatio: 0.005 },
},
},
],

View File

@@ -0,0 +1,31 @@
{
"~:id": "~u015fda4f-caa6-8103-8004-862a9e4b4d4b",
"~:file-id": "~u015fda4f-caa6-8103-8004-862a00dd4f31",
"~:created-at": "~m1718718436639",
"~:content": {
"~ue117f7f6-433c-807e-8004-862a38e1823d": {
"~:id": "~ue117f7f6-433c-807e-8004-862a38e1823d",
"~:name": "Button",
"~:path": "",
"~:modified-at": "~m1718718335855",
"~:main-instance-id": "~ue117f7f6-433c-807e-8004-862a38e0099a",
"~:main-instance-page": "~u015fda4f-caa6-8103-8004-862a00ddbe94"
},
"~ue117f7f6-433c-807e-8004-862a51a90ef5": {
"~:id": "~ue117f7f6-433c-807e-8004-862a51a90ef5",
"~:name": "Badge",
"~:path": "",
"~:modified-at": "~m1718718361245",
"~:main-instance-id": "~ue117f7f6-433c-807e-8004-862a51a84a91",
"~:main-instance-page": "~u015fda4f-caa6-8103-8004-862a00ddbe94"
},
"~ue117f7f6-433c-807e-8004-862a9b541a46": {
"~:id": "~ue117f7f6-433c-807e-8004-862a9b541a46",
"~:name": "Avatar",
"~:path": "",
"~:modified-at": "~m1718718436652",
"~:main-instance-id": "~ue117f7f6-433c-807e-8004-862a9b5374b6",
"~:main-instance-page": "~u015fda4f-caa6-8103-8004-862a00ddbe94"
}
}
}

View File

@@ -0,0 +1,630 @@
{
"~:id": "~u015fda4f-caa6-8103-8004-862a9e4ad279",
"~:file-id": "~u015fda4f-caa6-8103-8004-862a00dd4f31",
"~:created-at": "~m1718718436639",
"~:content": {
"~:options": {},
"~:objects": {
"~u00000000-0000-0000-0000-000000000000": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:name": "Root Frame",
"~:width": 0.01,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 0,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0.01
}
},
{
"~#point": {
"~:x": 0,
"~:y": 0.01
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~u00000000-0000-0000-0000-000000000000",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1.0,
"~:selrect": {
"~#rect": {
"~:x": 0,
"~:y": 0,
"~:width": 0.01,
"~:height": 0.01,
"~:x1": 0,
"~:y1": 0,
"~:x2": 0.01,
"~:y2": 0.01
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 0.01,
"~:flip-y": null,
"~:shapes": [
"~ue117f7f6-433c-807e-8004-862a38e0099a",
"~ue117f7f6-433c-807e-8004-862a51a84a91",
"~ue117f7f6-433c-807e-8004-862a9b5374b6"
]
}
},
"~ue117f7f6-433c-807e-8004-862a18bba46f": {
"~#shape": {
"~:y": 220,
"~:rx": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Button",
"~:width": 120,
"~:type": "~:rect",
"~:points": [
{
"~#point": {
"~:x": 663,
"~:y": 220
}
},
{
"~#point": {
"~:x": 783,
"~:y": 220
}
},
{
"~#point": {
"~:x": 783,
"~:y": 274
}
},
{
"~#point": {
"~:x": 663,
"~:y": 274
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:constraints-v": "~:scale",
"~:constraints-h": "~:scale",
"~:id": "~ue117f7f6-433c-807e-8004-862a18bba46f",
"~:parent-id": "~ue117f7f6-433c-807e-8004-862a38e0099a",
"~:frame-id": "~ue117f7f6-433c-807e-8004-862a38e0099a",
"~:strokes": [],
"~:x": 663,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 663,
"~:y": 220,
"~:width": 120,
"~:height": 54,
"~:x1": 663,
"~:y1": 220,
"~:x2": 783,
"~:y2": 274
}
},
"~:fills": [
{
"~:fill-color": "#B1B2B5",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:ry": 0,
"~:height": 54,
"~:flip-y": null
}
},
"~ue117f7f6-433c-807e-8004-862a38e0099a": {
"~#shape": {
"~:y": 220,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:hide-in-viewer": true,
"~:name": "Button",
"~:width": 120,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 663,
"~:y": 220
}
},
{
"~#point": {
"~:x": 783,
"~:y": 220
}
},
{
"~#point": {
"~:x": 783,
"~:y": 274
}
},
{
"~#point": {
"~:x": 663,
"~:y": 274
}
}
],
"~:component-root": true,
"~:show-content": true,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~ue117f7f6-433c-807e-8004-862a38e0099a",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:component-id": "~ue117f7f6-433c-807e-8004-862a38e1823d",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 663,
"~:main-instance": true,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 663,
"~:y": 220,
"~:width": 120,
"~:height": 54,
"~:x1": 663,
"~:y1": 220,
"~:x2": 783,
"~:y2": 274
}
},
"~:fills": [],
"~:flip-x": null,
"~:height": 54,
"~:component-file": "~u015fda4f-caa6-8103-8004-862a00dd4f31",
"~:flip-y": null,
"~:shapes": [
"~ue117f7f6-433c-807e-8004-862a18bba46f"
]
}
},
"~ue117f7f6-433c-807e-8004-862a40b7caca": {
"~#shape": {
"~:y": 188,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Badge",
"~:width": 61,
"~:type": "~:circle",
"~:points": [
{
"~#point": {
"~:x": 860,
"~:y": 188
}
},
{
"~#point": {
"~:x": 921,
"~:y": 188
}
},
{
"~#point": {
"~:x": 921,
"~:y": 247
}
},
{
"~#point": {
"~:x": 860,
"~:y": 247
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:constraints-v": "~:scale",
"~:constraints-h": "~:scale",
"~:id": "~ue117f7f6-433c-807e-8004-862a40b7caca",
"~:parent-id": "~ue117f7f6-433c-807e-8004-862a51a84a91",
"~:frame-id": "~ue117f7f6-433c-807e-8004-862a51a84a91",
"~:strokes": [],
"~:x": 860,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 860,
"~:y": 188,
"~:width": 61,
"~:height": 59,
"~:x1": 860,
"~:y1": 188,
"~:x2": 921,
"~:y2": 247
}
},
"~:fills": [
{
"~:fill-color": "#7798ff",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 59,
"~:flip-y": null
}
},
"~ue117f7f6-433c-807e-8004-862a51a84a91": {
"~#shape": {
"~:y": 188,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:hide-in-viewer": true,
"~:name": "Badge",
"~:width": 61,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 860,
"~:y": 188
}
},
{
"~#point": {
"~:x": 921,
"~:y": 188
}
},
{
"~#point": {
"~:x": 921,
"~:y": 247
}
},
{
"~#point": {
"~:x": 860,
"~:y": 247
}
}
],
"~:component-root": true,
"~:show-content": true,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~ue117f7f6-433c-807e-8004-862a51a84a91",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:component-id": "~ue117f7f6-433c-807e-8004-862a51a90ef5",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 860,
"~:main-instance": true,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 860,
"~:y": 188,
"~:width": 61,
"~:height": 59,
"~:x1": 860,
"~:y1": 188,
"~:x2": 921,
"~:y2": 247
}
},
"~:fills": [],
"~:flip-x": null,
"~:height": 59,
"~:component-file": "~u015fda4f-caa6-8103-8004-862a00dd4f31",
"~:flip-y": null,
"~:shapes": [
"~ue117f7f6-433c-807e-8004-862a40b7caca"
]
}
},
"~ue117f7f6-433c-807e-8004-862a8c166257": {
"~#shape": {
"~:y": 97,
"~:rx": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Avatar",
"~:width": 66,
"~:type": "~:rect",
"~:points": [
{
"~#point": {
"~:x": 554,
"~:y": 97
}
},
{
"~#point": {
"~:x": 620,
"~:y": 97
}
},
{
"~#point": {
"~:x": 620,
"~:y": 163
}
},
{
"~#point": {
"~:x": 554,
"~:y": 163
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:constraints-v": "~:scale",
"~:constraints-h": "~:scale",
"~:id": "~ue117f7f6-433c-807e-8004-862a8c166257",
"~:parent-id": "~ue117f7f6-433c-807e-8004-862a9b5374b6",
"~:frame-id": "~ue117f7f6-433c-807e-8004-862a9b5374b6",
"~:strokes": [],
"~:x": 554,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 554,
"~:y": 97,
"~:width": 66,
"~:height": 66,
"~:x1": 554,
"~:y1": 97,
"~:x2": 620,
"~:y2": 163
}
},
"~:fills": [
{
"~:fill-color": "#ff6ffc",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:ry": 0,
"~:height": 66,
"~:flip-y": null
}
},
"~ue117f7f6-433c-807e-8004-862a9b5374b6": {
"~#shape": {
"~:y": 97,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:hide-in-viewer": true,
"~:name": "Avatar",
"~:width": 66,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 554,
"~:y": 97
}
},
{
"~#point": {
"~:x": 620,
"~:y": 97
}
},
{
"~#point": {
"~:x": 620,
"~:y": 163
}
},
{
"~#point": {
"~:x": 554,
"~:y": 163
}
}
],
"~:component-root": true,
"~:show-content": true,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~ue117f7f6-433c-807e-8004-862a9b5374b6",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:component-id": "~ue117f7f6-433c-807e-8004-862a9b541a46",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 554,
"~:main-instance": true,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 554,
"~:y": 97,
"~:width": 66,
"~:height": 66,
"~:x1": 554,
"~:y1": 97,
"~:x2": 620,
"~:y2": 163
}
},
"~:fills": [],
"~:flip-x": null,
"~:height": 66,
"~:component-file": "~u015fda4f-caa6-8103-8004-862a00dd4f31",
"~:flip-y": null,
"~:shapes": [
"~ue117f7f6-433c-807e-8004-862a8c166257"
]
}
}
},
"~:id": "~u015fda4f-caa6-8103-8004-862a00ddbe94",
"~:name": "Page 1"
}
}

View File

@@ -0,0 +1,105 @@
{
"~:features":{
"~#set":[
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions":{
"~:type":"~:membership",
"~:is-owner":true,
"~:is-admin":true,
"~:can-edit":true,
"~:can-read":true,
"~:is-logged":true
},
"~:has-media-trimmed":false,
"~:comment-thread-seqn":0,
"~:name":"Lorem ipsum",
"~:revn":14,
"~:modified-at":"~m1718718464651",
"~:id":"~u015fda4f-caa6-8103-8004-862a00dd4f31",
"~:is-shared":false,
"~:version":49,
"~:project-id":"~u0515a066-e303-8169-8004-73eb401b5d55",
"~:created-at":"~m1718718275492",
"~:data":{
"~:colors":{
"~ue117f7f6-433c-807e-8004-862aa7732f9c":{
"~:path":"",
"~:color":"#ff6ffc",
"~:name":"Rosita",
"~:modified-at":"~m1718718452317",
"~:opacity":1,
"~:id":"~ue117f7f6-433c-807e-8004-862aa7732f9c"
},
"~ue117f7f6-433c-807e-8004-862ab306fa2b":{
"~:path":"",
"~:color":"#7798ff",
"~:name":"#7798ff",
"~:modified-at":"~m1718718461420",
"~:opacity":1,
"~:id":"~ue117f7f6-433c-807e-8004-862ab306fa2b"
}
},
"~:typographies":{
"~ue117f7f6-433c-807e-8004-862ab6ae29d8":{
"~:line-height":"1.2",
"~:font-style":"normal",
"~:text-transform":"none",
"~:font-id":"sourcesanspro",
"~:font-size":"14",
"~:font-weight":"400",
"~:name":"Source Sans Pro Regular",
"~:modified-at":"~m1718718464655",
"~:font-variant-id":"regular",
"~:id":"~ue117f7f6-433c-807e-8004-862ab6ae29d8",
"~:letter-spacing":"0",
"~:font-family":"sourcesanspro"
}
},
"~:pages":[
"~u015fda4f-caa6-8103-8004-862a00ddbe94"
],
"~:components":{
"~#penpot/pointer":[
"~u015fda4f-caa6-8103-8004-862a9e4b4d4b",
{
"~:created-at":"~m1718718436653"
}
]
},
"~:id":"~u015fda4f-caa6-8103-8004-862a00dd4f31",
"~:options":{
"~:components-v2":true
},
"~:recent-colors":[
{
"~:color":"#b5b1b4",
"~:opacity":1
},
{
"~:color":"#ff6ffc",
"~:opacity":1
},
{
"~:color":"#7798ff",
"~:opacity":1
}
],
"~:pages-index":{
"~u015fda4f-caa6-8103-8004-862a00ddbe94":{
"~#penpot/pointer":[
"~u015fda4f-caa6-8103-8004-862a9e4ad279",
{
"~:created-at":"~m1718718436653"
}
]
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"~:id": "~u62edaeb8-e212-81ca-8004-80a6f8a42e8e",
"~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
"~:created-at": "~m1718348381840",
"~:updated-at": "~m1718348381840",
"~:name": "new token",
"~:token": "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.9aFN5YdOI-b-NQPos5uqF8J8b9iMyeri3yYhV5FlHuhNbRwk0YuftA.Dygx9O5-KsAHpuqD.ryTDCqelYOk1XYflTlDGFlzG8VLuElKHSGHdJyJvWqcCUANWzl8cVvezvU2GWg1Piin21KNrcV0TEcHPpDggySRbTn01MOIjw3vTVHdGrlHaVq5VpnWb5hCfs_P9kF7Y2IWOa4da4mM.IulvBQUllnay7clORd-NSg"
}

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,8 @@
[
{
"~:id": "~u62edaeb8-e212-81ca-8004-80a6f8a42e8e",
"~:name": "new token",
"~:created-at": "~m1718348381840",
"~:updated-at": "~m1718348381840"
}
]

View File

@@ -0,0 +1,15 @@
[
{
"~:font-style": "normal",
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
"~:font-id": "~u838cda51-c50f-8032-8004-6ac92ea6eaea",
"~:font-weight": 400,
"~:ttf-file-id": "~ue3710e43-7e40-405d-a4ea-8bb85443d44b",
"~:modified-at": "~m1716880956479",
"~:otf-file-id": "~u72bd3cda-478a-4e0e-a372-4a4f7cdc1371",
"~:id": "~u28f4b65f-3667-8087-8004-6ac93050433a",
"~:woff1-file-id": "~ua4c0a056-2eb6-47cc-bf80-3115d14e048d",
"~:created-at": "~m1716880956479",
"~:font-family": "Milligram Variable Trial"
}
]

View File

@@ -0,0 +1,19 @@
[{
"~:id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7",
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
"~:created-at": "~m1715266551088",
"~:modified-at": "~m1715266551088",
"~:is-default": false,
"~:name": "New Project 1",
"~:is-pinned": false,
"~:count": 1
},
{
"~:id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
"~:created-at": "~m1713533116382",
"~:modified-at": "~m1713873823633",
"~:is-default": true,
"~:name": "Drafts",
"~:count": 1
}]

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,219 @@
{
"~#set": [
{
"~:name": "New File 3",
"~:revn": 1,
"~:id": "~u28f4b65f-3667-8087-8004-69eca173cc07",
"~:is-shared": true,
"~:project-id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7",
"~:created-at": "~m1713518796912",
"~:modified-at": "~m1713519762931",
"~:library-summary": {
"~:components": {
"~:count": 1,
"~:sample": [
{
"~:id": "~ua30724ae-f8d8-8003-8004-69ecacfc8a4c",
"~:name": "Rectangle",
"~:path": "",
"~:modified-at": "~m1716823150739",
"~:main-instance-id": "~ua30724ae-f8d8-8003-8004-69ecacfa2045",
"~:main-instance-page": "~u28f4b65f-3667-8087-8004-69eca173cc08",
"~:objects": {
"~ua30724ae-f8d8-8003-8004-69ecacfa2045": {
"~#shape": {
"~:y": 168,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:hide-in-viewer": true,
"~:name": "Rectangle",
"~:width": 553,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 481,
"~:y": 168
}
},
{
"~#point": {
"~:x": 1034,
"~:y": 168
}
},
{
"~#point": {
"~:x": 1034,
"~:y": 550
}
},
{
"~#point": {
"~:x": 481,
"~:y": 550
}
}
],
"~:component-root": true,
"~:show-content": true,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~ua30724ae-f8d8-8003-8004-69ecacfa2045",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:component-id": "~ua30724ae-f8d8-8003-8004-69ecacfc8a4c",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 481,
"~:main-instance": true,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 481,
"~:y": 168,
"~:width": 553,
"~:height": 382,
"~:x1": 481,
"~:y1": 168,
"~:x2": 1034,
"~:y2": 550
}
},
"~:fills": [],
"~:flip-x": null,
"~:height": 382,
"~:component-file": "~u28f4b65f-3667-8087-8004-69eca173cc07",
"~:flip-y": null,
"~:shapes": [
"~ua30724ae-f8d8-8003-8004-69eca9b27c8c"
]
}
},
"~ua30724ae-f8d8-8003-8004-69eca9b27c8c": {
"~#shape": {
"~:y": 168,
"~:rx": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Rectangle",
"~:width": 553,
"~:type": "~:rect",
"~:points": [
{
"~#point": {
"~:x": 481,
"~:y": 168
}
},
{
"~#point": {
"~:x": 1034,
"~:y": 168
}
},
{
"~#point": {
"~:x": 1034,
"~:y": 550
}
},
{
"~#point": {
"~:x": 481,
"~:y": 550
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:constraints-v": "~:scale",
"~:constraints-h": "~:scale",
"~:id": "~ua30724ae-f8d8-8003-8004-69eca9b27c8c",
"~:parent-id": "~ua30724ae-f8d8-8003-8004-69ecacfa2045",
"~:frame-id": "~ua30724ae-f8d8-8003-8004-69ecacfa2045",
"~:strokes": [],
"~:x": 481,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 481,
"~:y": 168,
"~:width": 553,
"~:height": 382,
"~:x1": 481,
"~:y1": 168,
"~:x2": 1034,
"~:y2": 550
}
},
"~:fills": [
{
"~:fill-color": "#B1B2B5",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:ry": 0,
"~:height": 382,
"~:flip-y": null
}
}
}
}
]
},
"~:media": {
"~:count": 0,
"~:sample": []
},
"~:colors": {
"~:count": 0,
"~:sample": []
},
"~:typographies": {
"~:count": 0,
"~:sample": []
}
}
}
]
}

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,6 @@
[
{ "~:email": "test1@mail.com", "~:role": "~:editor", "~:expired": true },
{ "~:email": "test2@mail.com", "~:role": "~:editor", "~:expired": false },
{ "~:email": "test3@mail.com", "~:role": "~:admin", "~:expired": true },
{ "~:email": "test4@mail.com", "~:role": "~:admin", "~:expired": false }
]

View File

@@ -0,0 +1,16 @@
[
{
"~:is-admin": true,
"~:email": "foo@example.com",
"~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3",
"~:name": "Princesa Leia",
"~:fullname": "Princesa Leia",
"~:is-owner": false,
"~:modified-at": "~m1713533116365",
"~:can-edit": true,
"~:is-active": true,
"~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
"~:profile-id": "~uf56647eb-19a7-8115-8003-b6bc939ecd1b",
"~:created-at": "~m1713533116365"
}
]

View File

@@ -0,0 +1,29 @@
[
{
"~:id": "~u8b479b80-e02d-8074-8004-4088dc6bfd11",
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:created-at": "~m1714045521389",
"~:modified-at": "~m1714045654874",
"~:name": "New File 2",
"~:revn": 1,
"~:is-shared": false
},
{
"~:id": "~u95d6fdd8-48d8-8148-8004-38af910d2dbe",
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:created-at": "~m1713518796912",
"~:modified-at": "~m1713519762931",
"~:name": "New File 1",
"~:revn": 1,
"~:is-shared": false
},
{
"~:id": "~u28f4b65f-3667-8087-8004-69eca173cc07",
"~:project-id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7",
"~:created-at": "~m1713518796912",
"~:modified-at": "~m1713519762931",
"~:name": "New File 3",
"~:revn": 1,
"~:is-shared": true
}
]

View File

@@ -0,0 +1 @@
{"~:projects":1,"~:files":3}

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,20 @@
[
{
"~:id": "~u29ce7ec9-e75d-81b4-8004-08100373558a",
"~:uri": {
"~#uri": "https://www.abc.es"
},
"~:mtype": "application/json",
"~:is-active": false,
"~:error-count": 0
},
{
"~:id": "~u43d6b3b1-40f7-807b-8003-f9846292b4c7",
"~:uri": {
"~#uri": "https://www.google.com"
},
"~:mtype": "application/json",
"~:is-active": true,
"~:error-count": 0
}
]

View File

@@ -0,0 +1,29 @@
[
{
"~:id": "~u8b479b80-e02d-8074-8004-4088dc6bfd11",
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:created-at": "~m1714045521389",
"~:modified-at": "~m1714045654874",
"~:name": "New File 2",
"~:revn": 1,
"~:is-shared": false
},
{
"~:id": "~u95d6fdd8-48d8-8148-8004-38af910d2dbe",
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:created-at": "~m1713518796912",
"~:modified-at": "~m1713519762931",
"~:name": "New File 1",
"~:revn": 1,
"~:is-shared": false
},
{
"~:id": "~u28f4b65f-3667-8087-8004-69eca173cc07",
"~:project-id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7",
"~:created-at": "~m1713518796912",
"~:modified-at": "~m1713519762931",
"~:name": "New File 3",
"~:revn": 1,
"~:is-shared": true
}
]

View File

@@ -0,0 +1,363 @@
{
"~:id": "~u03bff843-920f-81a1-8004-7563acdc8ca1",
"~:file-id": "~u03bff843-920f-81a1-8004-756365e1eb6a",
"~:created-at": "~m1717592543081",
"~:content": {
"~:options": {},
"~:objects": {
"~u00000000-0000-0000-0000-000000000000": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:name": "Root Frame",
"~:width": 0.01,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 0,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0.01
}
},
{
"~#point": {
"~:x": 0,
"~:y": 0.01
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~u00000000-0000-0000-0000-000000000000",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1.0,
"~:selrect": {
"~#rect": {
"~:x": 0,
"~:y": 0,
"~:width": 0.01,
"~:height": 0.01,
"~:x1": 0,
"~:y1": 0,
"~:x2": 0.01,
"~:y2": 0.01
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 0.01,
"~:flip-y": null,
"~:shapes": [
"~ub574c052-1a31-80bb-8004-75636879759b"
]
}
},
"~ub574c052-1a31-80bb-8004-75636879759b": {
"~#shape": {
"~:y": 128,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Board",
"~:width": 256,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 128,
"~:y": 128
}
},
{
"~#point": {
"~:x": 384,
"~:y": 128
}
},
{
"~#point": {
"~:x": 384,
"~:y": 384
}
},
{
"~#point": {
"~:x": 128,
"~:y": 384
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~ub574c052-1a31-80bb-8004-75636879759b",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 128,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 128,
"~:y": 128,
"~:width": 256,
"~:height": 256,
"~:x1": 128,
"~:y1": 128,
"~:x2": 384,
"~:y2": 384
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 256,
"~:flip-y": null,
"~:shapes": [
"~ub574c052-1a31-80bb-8004-75636a9b8205",
"~ub574c052-1a31-80bb-8004-756392461069"
]
}
},
"~ub574c052-1a31-80bb-8004-75636a9b8205": {
"~#shape": {
"~:y": 136,
"~:rx": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Rectangle",
"~:width": 64,
"~:type": "~:rect",
"~:points": [
{
"~#point": {
"~:x": 136,
"~:y": 136
}
},
{
"~#point": {
"~:x": 200,
"~:y": 136
}
},
{
"~#point": {
"~:x": 200,
"~:y": 199.99999999999997
}
},
{
"~#point": {
"~:x": 136,
"~:y": 199.99999999999997
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~ub574c052-1a31-80bb-8004-75636a9b8205",
"~:parent-id": "~ub574c052-1a31-80bb-8004-75636879759b",
"~:frame-id": "~ub574c052-1a31-80bb-8004-75636879759b",
"~:strokes": [],
"~:x": 136,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 136,
"~:y": 136,
"~:width": 64,
"~:height": 63.99999999999997,
"~:x1": 136,
"~:y1": 136,
"~:x2": 200,
"~:y2": 199.99999999999997
}
},
"~:fills": [
{
"~:fill-color": "#B1B2B5",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:ry": 0,
"~:height": 63.99999999999997,
"~:flip-y": null
}
},
"~ub574c052-1a31-80bb-8004-756392461069": {
"~#shape": {
"~:y": 136,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Ellipse",
"~:width": 64,
"~:type": "~:circle",
"~:points": [
{
"~#point": {
"~:x": 256,
"~:y": 136
}
},
{
"~#point": {
"~:x": 320,
"~:y": 136
}
},
{
"~#point": {
"~:x": 320,
"~:y": 200
}
},
{
"~#point": {
"~:x": 256,
"~:y": 200
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:constraints-v": "~:bottom",
"~:constraints-h": "~:right",
"~:id": "~ub574c052-1a31-80bb-8004-756392461069",
"~:parent-id": "~ub574c052-1a31-80bb-8004-75636879759b",
"~:frame-id": "~ub574c052-1a31-80bb-8004-75636879759b",
"~:strokes": [],
"~:x": 256,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 256,
"~:y": 136,
"~:width": 64,
"~:height": 64,
"~:x1": 256,
"~:y1": 136,
"~:x2": 320,
"~:y2": 200
}
},
"~:fills": [
{
"~:fill-color": "#B1B2B5",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 64,
"~:flip-y": null
}
}
},
"~:id": "~u03bff843-920f-81a1-8004-756365e1eb6b",
"~:name": "Page 1"
}
}

View File

@@ -0,0 +1,343 @@
{
"~:features":{
"~#set":[
"layout/grid",
"styles/v2",
"fdata/shape-data-type"
]
},
"~:permissions":{
"~:type":"~:membership",
"~:is-owner":true,
"~:is-admin":true,
"~:can-edit":true,
"~:can-read":true,
"~:is-logged":true
},
"~:has-media-trimmed":false,
"~:comment-thread-seqn":0,
"~:name":"New File 12",
"~:revn":2,
"~:modified-at":"~m1718012938567",
"~:id":"~u1795a568-0df0-8095-8004-7ba741f56be2",
"~:is-shared":false,
"~:version":48,
"~:project-id":"~u4dc640b0-5cbf-11ec-a7c5-91e9eb4f238d",
"~:created-at":"~m1718012912598",
"~:data":{
"~:pages":[
"~u1795a568-0df0-8095-8004-7ba741f56be3"
],
"~:pages-index":{
"~u1795a568-0df0-8095-8004-7ba741f56be3":{
"~:options":{
},
"~:objects":{
"~u00000000-0000-0000-0000-000000000000":{
"~#shape":{
"~:y":0,
"~:hide-fill-on-export":false,
"~:transform":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:rotation":0,
"~:name":"Root Frame",
"~:width":0.01,
"~:type":"~:frame",
"~:points":[
{
"~#point":{
"~:x":0,
"~:y":0
}
},
{
"~#point":{
"~:x":0.01,
"~:y":0
}
},
{
"~#point":{
"~:x":0.01,
"~:y":0.01
}
},
{
"~#point":{
"~:x":0,
"~:y":0.01
}
}
],
"~:proportion-lock":false,
"~:transform-inverse":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:id":"~u00000000-0000-0000-0000-000000000000",
"~:parent-id":"~u00000000-0000-0000-0000-000000000000",
"~:frame-id":"~u00000000-0000-0000-0000-000000000000",
"~:strokes":[
],
"~:x":0,
"~:proportion":1.0,
"~:selrect":{
"~#rect":{
"~:x":0,
"~:y":0,
"~:width":0.01,
"~:height":0.01,
"~:x1":0,
"~:y1":0,
"~:x2":0.01,
"~:y2":0.01
}
},
"~:fills":[
{
"~:fill-color":"#FFFFFF",
"~:fill-opacity":1
}
],
"~:flip-x":null,
"~:height":0.01,
"~:flip-y":null,
"~:shapes":[
"~u2ace9ce8-8e01-8086-8004-7ba745d4305a",
"~u2ace9ce8-8e01-8086-8004-7ba748566e02"
]
}
},
"~u2ace9ce8-8e01-8086-8004-7ba745d4305a":{
"~#shape":{
"~:y":221,
"~:rx":0,
"~:transform":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:rotation":0,
"~:grow-type":"~:fixed",
"~:hide-in-viewer":false,
"~:name":"Rectangle",
"~:width":105,
"~:type":"~:rect",
"~:points":[
{
"~#point":{
"~:x":165,
"~:y":221
}
},
{
"~#point":{
"~:x":270,
"~:y":221
}
},
{
"~#point":{
"~:x":270,
"~:y":316
}
},
{
"~#point":{
"~:x":165,
"~:y":316
}
}
],
"~:proportion-lock":false,
"~:transform-inverse":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:id":"~u2ace9ce8-8e01-8086-8004-7ba745d4305a",
"~:parent-id":"~u00000000-0000-0000-0000-000000000000",
"~:frame-id":"~u00000000-0000-0000-0000-000000000000",
"~:strokes":[
],
"~:x":165,
"~:proportion":1,
"~:selrect":{
"~#rect":{
"~:x":165,
"~:y":221,
"~:width":105,
"~:height":95,
"~:x1":165,
"~:y1":221,
"~:x2":270,
"~:y2":316
}
},
"~:fills":[
{
"~:fill-color":"#B1B2B5",
"~:fill-opacity":1
}
],
"~:flip-x":null,
"~:ry":0,
"~:height":95,
"~:flip-y":null
}
},
"~u2ace9ce8-8e01-8086-8004-7ba748566e02":{
"~#shape":{
"~:y":228,
"~:transform":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:rotation":0,
"~:grow-type":"~:fixed",
"~:hide-in-viewer":false,
"~:name":"Ellipse",
"~:width":85,
"~:type":"~:circle",
"~:points":[
{
"~#point":{
"~:x":344,
"~:y":228
}
},
{
"~#point":{
"~:x":429,
"~:y":228
}
},
{
"~#point":{
"~:x":429,
"~:y":308
}
},
{
"~#point":{
"~:x":344,
"~:y":308
}
}
],
"~:proportion-lock":false,
"~:transform-inverse":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:blur":{
"~:id":"~u2ace9ce8-8e01-8086-8004-7ba757cdd271",
"~:type":"~:layer-blur",
"~:value":4,
"~:hidden":false
},
"~:id":"~u2ace9ce8-8e01-8086-8004-7ba748566e02",
"~:parent-id":"~u00000000-0000-0000-0000-000000000000",
"~:frame-id":"~u00000000-0000-0000-0000-000000000000",
"~:strokes":[
{
"~:stroke-alignment":"~:inner",
"~:stroke-style":"~:solid",
"~:stroke-color":"#000000",
"~:stroke-opacity":1,
"~:stroke-width":1
}
],
"~:x":344,
"~:proportion":1,
"~:shadow":[
{
"~:color":{
"~:color":"#000000",
"~:opacity":0.2
},
"~:spread":0,
"~:offset-y":4,
"~:style":"~:drop-shadow",
"~:blur":4,
"~:hidden":false,
"~:id":"~u2ace9ce8-8e01-8086-8004-7ba756ddebd5",
"~:offset-x":4
}
],
"~:selrect":{
"~#rect":{
"~:x":344,
"~:y":228,
"~:width":85,
"~:height":80,
"~:x1":344,
"~:y1":228,
"~:x2":429,
"~:y2":308
}
},
"~:fills":[
{
"~:fill-color":"#1247e7",
"~:fill-opacity":1
}
],
"~:flip-x":null,
"~:height":80,
"~:flip-y":null
}
}
},
"~:id":"~u1795a568-0df0-8095-8004-7ba741f56be3",
"~:name":"Page 1"
}
},
"~:id":"~u1795a568-0df0-8095-8004-7ba741f56be2",
"~:recent-colors":[
{
"~:color":"#1247e7",
"~:opacity":1
}
]
}
}

View File

@@ -0,0 +1,49 @@
{
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true,
"~:can-read": true,
"~:is-logged": true
},
"~:has-media-trimmed": false,
"~:comment-thread-seqn": 0,
"~:name": "New File 2",
"~:revn": 9,
"~:modified-at": "~m1717592543083",
"~:id": "~u03bff843-920f-81a1-8004-756365e1eb6a",
"~:is-shared": false,
"~:version": 48,
"~:project-id": "~u0515a066-e303-8169-8004-73eb401b5d55",
"~:created-at": "~m1717592470408",
"~:data": {
"~:pages": [
"~u03bff843-920f-81a1-8004-756365e1eb6b"
],
"~:pages-index": {
"~u03bff843-920f-81a1-8004-756365e1eb6b": {
"~#penpot/pointer": [
"~u03bff843-920f-81a1-8004-7563acdc8ca1",
{
"~:created-at": "~m1717592543090"
}
]
}
},
"~:id": "~u03bff843-920f-81a1-8004-756365e1eb6a",
"~:options": {
"~:components-v2": true
}
}
}

View File

@@ -0,0 +1,3 @@
{
"03bff843-920f-81a1-8004-756365e1eb6a/03bff843-920f-81a1-8004-756365e1eb6b/b574c052-1a31-80bb-8004-75636879759b/frame": "http://localhost:3449/assets/by-id/bdc9e592-f685-4b08-9a44-127ce20efee6"
}

View File

@@ -0,0 +1,48 @@
[
{
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true
},
"~:name": "Default",
"~:modified-at": "~m1713533116375",
"~:id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
"~:created-at": "~m1713533116375",
"~:is-default": true
},
{
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true
},
"~:name": "Second team",
"~:modified-at": "~m1701164272671",
"~:id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3",
"~:created-at": "~m1701164272671",
"~:is-default": false
}
]

View File

@@ -0,0 +1,97 @@
{
"~:id": "~u0515a066-e303-8169-8004-73eb58e899c2",
"~:file-id": "~uc7ce0794-0992-8105-8004-38f280443849",
"~:created-at": "~m1717493890966",
"~:content": {
"~:options": {},
"~:objects": {
"~u00000000-0000-0000-0000-000000000000": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:name": "Root Frame",
"~:width": 0.01,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 0,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0.01
}
},
{
"~#point": {
"~:x": 0,
"~:y": 0.01
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~u00000000-0000-0000-0000-000000000000",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1.0,
"~:selrect": {
"~#rect": {
"~:x": 0,
"~:y": 0,
"~:width": 0.01,
"~:height": 0.01,
"~:x1": 0,
"~:y1": 0,
"~:x2": 0.01,
"~:y2": 0.01
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 0.01,
"~:flip-y": null,
"~:shapes": []
}
}
},
"~:id": "~uc7ce0794-0992-8105-8004-38f28044384a",
"~:name": "Page 1"
}
}

View File

@@ -0,0 +1,186 @@
{
"~:id": "~udd5cc0bb-91ff-81b9-8004-77dfae2d9e7c",
"~:file-id": "~udd5cc0bb-91ff-81b9-8004-77df9cd3edb1",
"~:created-at": "~m1717759268004",
"~:content": {
"~:options": {},
"~:objects": {
"~u00000000-0000-0000-0000-000000000000": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:name": "Root Frame",
"~:width": 0.01,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 0,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0.01
}
},
{
"~#point": {
"~:x": 0,
"~:y": 0.01
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~u00000000-0000-0000-0000-000000000000",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1.0,
"~:selrect": {
"~#rect": {
"~:x": 0,
"~:y": 0,
"~:width": 0.01,
"~:height": 0.01,
"~:x1": 0,
"~:y1": 0,
"~:x2": 0.01,
"~:y2": 0.01
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 0.01,
"~:flip-y": null,
"~:shapes": [
"~uec508673-9e3b-80bf-8004-77dfa30a2b13"
]
}
},
"~uec508673-9e3b-80bf-8004-77dfa30a2b13": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Board",
"~:width": 256.00000000000006,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 0,
"~:y": 0
}
},
{
"~#point": {
"~:x": 256.00000000000006,
"~:y": 0
}
},
{
"~#point": {
"~:x": 256.00000000000006,
"~:y": 256
}
},
{
"~#point": {
"~:x": 0,
"~:y": 256
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~uec508673-9e3b-80bf-8004-77dfa30a2b13",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 0,
"~:y": 0,
"~:width": 256.00000000000006,
"~:height": 256,
"~:x1": 0,
"~:y1": 0,
"~:x2": 256.00000000000006,
"~:y2": 256
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 256,
"~:flip-y": null,
"~:shapes": []
}
}
},
"~:id": "~udd5cc0bb-91ff-81b9-8004-77df9cd3edb2",
"~:name": "Page 1"
}
}

View File

@@ -0,0 +1,86 @@
{
"~:users": [
{
"~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
"~:email": "leia@example.com",
"~:name": "Princesa Leia",
"~:fullname": "Princesa Leia",
"~:is-active": true
}
],
"~:fonts": [],
"~:project": {
"~:id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:name": "Drafts",
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d"
},
"~:share-links": [],
"~:libraries": [],
"~:file": {
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:has-media-trimmed": false,
"~:comment-thread-seqn": 0,
"~:name": "New File 1",
"~:revn": 0,
"~:modified-at": "~m1717493891000",
"~:id": "~uc7ce0794-0992-8105-8004-38f280443849",
"~:is-shared": false,
"~:version": 48,
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:created-at": "~m1717493891000",
"~:data": {
"~:id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:options": {
"~:components-v2": true
},
"~:pages": [
"~uc7ce0794-0992-8105-8004-38f28044384a"
],
"~:pages-index": {
"~uc7ce0794-0992-8105-8004-38f28044384a": {
"~#penpot/pointer": [
"~u0515a066-e303-8169-8004-73eb58e899c2",
{
"~:created-at": "~m1717493890978"
}
]
}
}
}
},
"~:team": {
"~:id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
"~:created-at": "~m1717493865581",
"~:modified-at": "~m1717493865581",
"~:name": "Default",
"~:is-default": true,
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
}
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true,
"~:can-read": true,
"~:is-logged": true,
"~:in-team": true
}
}

View File

@@ -0,0 +1,86 @@
{
"~:users": [
{
"~:id": "~u0515a066-e303-8169-8004-73eb4018f4e0",
"~:email": "leia@example.com",
"~:name": "Princesa Leia",
"~:fullname": "Princesa Leia",
"~:is-active": true
}
],
"~:fonts": [],
"~:project": {
"~:id": "~u0515a066-e303-8169-8004-73eb401b5d55",
"~:name": "Drafts",
"~:team-id": "~u0515a066-e303-8169-8004-73eb401977a6"
},
"~:share-links": [],
"~:libraries": [],
"~:file": {
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:has-media-trimmed": false,
"~:comment-thread-seqn": 0,
"~:name": "New File 3",
"~:revn": 1,
"~:modified-at": "~m1717759268010",
"~:id": "~udd5cc0bb-91ff-81b9-8004-77df9cd3edb1",
"~:is-shared": false,
"~:version": 48,
"~:project-id": "~u0515a066-e303-8169-8004-73eb401b5d55",
"~:created-at": "~m1717759250257",
"~:data": {
"~:id": "~udd5cc0bb-91ff-81b9-8004-77df9cd3edb1",
"~:options": {
"~:components-v2": true
},
"~:pages": [
"~udd5cc0bb-91ff-81b9-8004-77df9cd3edb2"
],
"~:pages-index": {
"~udd5cc0bb-91ff-81b9-8004-77df9cd3edb2": {
"~#penpot/pointer": [
"~udd5cc0bb-91ff-81b9-8004-77dfae2d9e7c",
{
"~:created-at": "~m1717759268024"
}
]
}
}
}
},
"~:team": {
"~:id": "~u0515a066-e303-8169-8004-73eb401977a6",
"~:created-at": "~m1717493865581",
"~:modified-at": "~m1717493865581",
"~:name": "Default",
"~:is-default": true,
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
}
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true,
"~:can-read": true,
"~:is-logged": true,
"~:in-team": true
}
}

View File

@@ -0,0 +1 @@
{}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,49 @@
{
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true,
"~:can-read": true,
"~:is-logged": true
},
"~:has-media-trimmed": false,
"~:comment-thread-seqn": 0,
"~:name": "New File 6",
"~:revn": 5,
"~:modified-at": "~m1718094617219",
"~:id": "~ucd90e028-326a-80b4-8004-7cdec16ffad5",
"~:is-shared": false,
"~:version": 48,
"~:project-id": "~u128636f9-5e78-812b-8004-350dd1a8869a",
"~:created-at": "~m1718094569923",
"~:data": {
"~:pages": [
"~ucd90e028-326a-80b4-8004-7cdec16ffad6"
],
"~:pages-index": {
"~ucd90e028-326a-80b4-8004-7cdec16ffad6": {
"~#penpot/pointer": [
"~ucd90e028-326a-80b4-8004-7cdeefa23ece",
{
"~:created-at": "~m1718094617224"
}
]
}
},
"~:id": "~ucd90e028-326a-80b4-8004-7cdec16ffad5",
"~:options": {
"~:components-v2": true
}
}
}

View File

@@ -0,0 +1,383 @@
{
"~:id": "~ucd90e028-326a-80b4-8004-7cdeefa23ece",
"~:file-id": "~ucd90e028-326a-80b4-8004-7cdec16ffad5",
"~:created-at": "~m1718094617214",
"~:content": {
"~:options": {},
"~:objects": {
"~u00000000-0000-0000-0000-000000000000": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:name": "Root Frame",
"~:width": 0.01,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 0,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0.01
}
},
{
"~#point": {
"~:x": 0,
"~:y": 0.01
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~u00000000-0000-0000-0000-000000000000",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1.0,
"~:selrect": {
"~#rect": {
"~:x": 0,
"~:y": 0,
"~:width": 0.01,
"~:height": 0.01,
"~:x1": 0,
"~:y1": 0,
"~:x2": 0.01,
"~:y2": 0.01
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 0.01,
"~:flip-y": null,
"~:shapes": [
"~u86087f92-9a17-8067-8004-7cdec45bee43",
"~u86087f92-9a17-8067-8004-7cded1cbe70e"
]
}
},
"~u86087f92-9a17-8067-8004-7cdec45bee43": {
"~#shape": {
"~:y": 341,
"~:hide-fill-on-export": false,
"~:layout-gap-type": "~:multiple",
"~:layout-padding": {
"~:p1": 34,
"~:p2": 36,
"~:p3": 34,
"~:p4": 36
},
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:layout-wrap-type": "~:nowrap",
"~:grow-type": "~:fixed",
"~:layout": "~:flex",
"~:hide-in-viewer": false,
"~:name": "Flex Board",
"~:layout-align-items": "~:start",
"~:width": 176,
"~:layout-padding-type": "~:simple",
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 217,
"~:y": 341
}
},
{
"~#point": {
"~:x": 393,
"~:y": 341
}
},
{
"~#point": {
"~:x": 393,
"~:y": 511
}
},
{
"~#point": {
"~:x": 217,
"~:y": 511
}
}
],
"~:layout-item-h-sizing": "~:auto",
"~:proportion-lock": false,
"~:layout-gap": {
"~:row-gap": 0,
"~:column-gap": 0
},
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:layout-item-v-sizing": "~:auto",
"~:layout-justify-content": "~:start",
"~:id": "~u86087f92-9a17-8067-8004-7cdec45bee43",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:layout-flex-dir": "~:row",
"~:layout-align-content": "~:stretch",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 217,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 217,
"~:y": 341,
"~:width": 176,
"~:height": 170,
"~:x1": 217,
"~:y1": 341,
"~:x2": 393,
"~:y2": 511
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 170,
"~:flip-y": null,
"~:shapes": [
"~u86087f92-9a17-8067-8004-7cdec98dfa7f"
]
}
},
"~u86087f92-9a17-8067-8004-7cdec98dfa7f": {
"~#shape": {
"~:y": 375,
"~:rx": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Rectangle",
"~:width": 104,
"~:type": "~:rect",
"~:points": [
{
"~#point": {
"~:x": 253,
"~:y": 375
}
},
{
"~#point": {
"~:x": 357,
"~:y": 375
}
},
{
"~#point": {
"~:x": 357,
"~:y": 477
}
},
{
"~#point": {
"~:x": 253,
"~:y": 477
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~u86087f92-9a17-8067-8004-7cdec98dfa7f",
"~:parent-id": "~u86087f92-9a17-8067-8004-7cdec45bee43",
"~:frame-id": "~u86087f92-9a17-8067-8004-7cdec45bee43",
"~:strokes": [],
"~:x": 253,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 253,
"~:y": 375,
"~:width": 104,
"~:height": 102,
"~:x1": 253,
"~:y1": 375,
"~:x2": 357,
"~:y2": 477
}
},
"~:fills": [
{
"~:fill-color": "#B1B2B5",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:ry": 0,
"~:height": 102,
"~:flip-y": null
}
},
"~u86087f92-9a17-8067-8004-7cded1cbe70e": {
"~#shape": {
"~:y": 300,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Container Board",
"~:width": 434,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 689,
"~:y": 300
}
},
{
"~#point": {
"~:x": 1123,
"~:y": 300
}
},
{
"~#point": {
"~:x": 1123,
"~:y": 741
}
},
{
"~#point": {
"~:x": 689,
"~:y": 741
}
}
],
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:id": "~u86087f92-9a17-8067-8004-7cded1cbe70e",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 689,
"~:proportion": 1,
"~:selrect": {
"~#rect": {
"~:x": 689,
"~:y": 300,
"~:width": 434,
"~:height": 441,
"~:x1": 689,
"~:y1": 300,
"~:x2": 1123,
"~:y2": 741
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 441,
"~:flip-y": null,
"~:shapes": []
}
}
},
"~:id": "~ucd90e028-326a-80b4-8004-7cdec16ffad6",
"~:name": "Page 1"
}
}

View File

@@ -0,0 +1,242 @@
{
"~:features":{
"~#set":[
"layout/grid",
"styles/v2",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions":{
"~:type":"~:membership",
"~:is-owner":true,
"~:is-admin":true,
"~:can-edit":true,
"~:can-read":true,
"~:is-logged":true
},
"~:has-media-trimmed":false,
"~:comment-thread-seqn":0,
"~:name":"Testing library 1",
"~:revn":2,
"~:modified-at":"~m1717512948250",
"~:id":"~uc1249a66-fce0-8175-8004-7433fe4be8bc",
"~:is-shared":true,
"~:version":48,
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:created-at":"~m1717512934704",
"~:data":{
"~:pages":[
"~uc1249a66-fce0-8175-8004-7433fe4be8bd"
],
"~:pages-index":{
"~uc1249a66-fce0-8175-8004-7433fe4be8bd":{
"~:options":{
},
"~:objects":{
"~u00000000-0000-0000-0000-000000000000":{
"~#shape":{
"~:y":0,
"~:hide-fill-on-export":false,
"~:transform":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:rotation":0,
"~:name":"Root Frame",
"~:width":0.01,
"~:type":"~:frame",
"~:points":[
{
"~#point":{
"~:x":0,
"~:y":0
}
},
{
"~#point":{
"~:x":0.01,
"~:y":0
}
},
{
"~#point":{
"~:x":0.01,
"~:y":0.01
}
},
{
"~#point":{
"~:x":0,
"~:y":0.01
}
}
],
"~:proportion-lock":false,
"~:transform-inverse":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:id":"~u00000000-0000-0000-0000-000000000000",
"~:parent-id":"~u00000000-0000-0000-0000-000000000000",
"~:frame-id":"~u00000000-0000-0000-0000-000000000000",
"~:strokes":[
],
"~:x":0,
"~:proportion":1.0,
"~:selrect":{
"~#rect":{
"~:x":0,
"~:y":0,
"~:width":0.01,
"~:height":0.01,
"~:x1":0,
"~:y1":0,
"~:x2":0.01,
"~:y2":0.01
}
},
"~:fills":[
{
"~:fill-color":"#FFFFFF",
"~:fill-opacity":1
}
],
"~:flip-x":null,
"~:height":0.01,
"~:flip-y":null,
"~:shapes":[
"~uc70224ec-c410-807b-8004-743400e00be8"
]
}
},
"~uc70224ec-c410-807b-8004-743400e00be8":{
"~#shape":{
"~:y":255,
"~:rx":0,
"~:transform":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:rotation":0,
"~:grow-type":"~:fixed",
"~:hide-in-viewer":false,
"~:name":"Rectangle",
"~:width":279.0000000000001,
"~:type":"~:rect",
"~:points":[
{
"~#point":{
"~:x":523,
"~:y":255
}
},
{
"~#point":{
"~:x":802.0000000000001,
"~:y":255
}
},
{
"~#point":{
"~:x":802.0000000000001,
"~:y":534
}
},
{
"~#point":{
"~:x":523,
"~:y":534
}
}
],
"~:proportion-lock":false,
"~:transform-inverse":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:id":"~uc70224ec-c410-807b-8004-743400e00be8",
"~:parent-id":"~u00000000-0000-0000-0000-000000000000",
"~:frame-id":"~u00000000-0000-0000-0000-000000000000",
"~:strokes":[
],
"~:x":523,
"~:proportion":1,
"~:selrect":{
"~#rect":{
"~:x":523,
"~:y":255,
"~:width":279.0000000000001,
"~:height":279,
"~:x1":523,
"~:y1":255,
"~:x2":802.0000000000001,
"~:y2":534
}
},
"~:fills":[
{
"~:fill-color":"#B1B2B5",
"~:fill-opacity":1
}
],
"~:flip-x":null,
"~:ry":0,
"~:height":279,
"~:flip-y":null
}
}
},
"~:id":"~uc1249a66-fce0-8175-8004-7433fe4be8bd",
"~:name":"Page 1"
}
},
"~:id":"~uc1249a66-fce0-8175-8004-7433fe4be8bc",
"~:options":{
"~:components-v2":true
},
"~:recent-colors":[
{
"~:color":"#187cd5",
"~:opacity":1
}
],
"~:colors":{
"~uc70224ec-c410-807b-8004-74340616cffb":{
"~:path":"",
"~:color":"#187cd5",
"~:name":"test-color-187cd5",
"~:modified-at":"~m1717512945259",
"~:opacity":1,
"~:id":"~uc70224ec-c410-807b-8004-74340616cffb"
}
}
}
}

View File

@@ -0,0 +1,222 @@
{
"~:features":{
"~#set":[
"layout/grid",
"styles/v2",
"fdata/shape-data-type"
]
},
"~:permissions":{
"~:type":"~:membership",
"~:is-owner":true,
"~:is-admin":true,
"~:can-edit":true,
"~:can-read":true,
"~:is-logged":true
},
"~:has-media-trimmed":false,
"~:comment-thread-seqn":0,
"~:name":"New File 14",
"~:revn":1,
"~:modified-at":"~m1718088151182",
"~:id":"~u6191cd35-bb1f-81f7-8004-7cc63d087374",
"~:is-shared":false,
"~:version":48,
"~:project-id":"~u4dc640b0-5cbf-11ec-a7c5-91e9eb4f238d",
"~:created-at":"~m1718088142886",
"~:data":{
"~:pages":[
"~u6191cd35-bb1f-81f7-8004-7cc63d087375"
],
"~:pages-index":{
"~u6191cd35-bb1f-81f7-8004-7cc63d087375":{
"~:options":{
},
"~:objects":{
"~u00000000-0000-0000-0000-000000000000":{
"~#shape":{
"~:y":0,
"~:hide-fill-on-export":false,
"~:transform":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:rotation":0,
"~:name":"Root Frame",
"~:width":0.01,
"~:type":"~:frame",
"~:points":[
{
"~#point":{
"~:x":0,
"~:y":0
}
},
{
"~#point":{
"~:x":0.01,
"~:y":0
}
},
{
"~#point":{
"~:x":0.01,
"~:y":0.01
}
},
{
"~#point":{
"~:x":0,
"~:y":0.01
}
}
],
"~:proportion-lock":false,
"~:transform-inverse":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:id":"~u00000000-0000-0000-0000-000000000000",
"~:parent-id":"~u00000000-0000-0000-0000-000000000000",
"~:frame-id":"~u00000000-0000-0000-0000-000000000000",
"~:strokes":[
],
"~:x":0,
"~:proportion":1.0,
"~:selrect":{
"~#rect":{
"~:x":0,
"~:y":0,
"~:width":0.01,
"~:height":0.01,
"~:x1":0,
"~:y1":0,
"~:x2":0.01,
"~:y2":0.01
}
},
"~:fills":[
{
"~:fill-color":"#FFFFFF",
"~:fill-opacity":1
}
],
"~:flip-x":null,
"~:height":0.01,
"~:flip-y":null,
"~:shapes":[
"~u7c75e310-c3a2-80fd-8004-7cc641479aef"
]
}
},
"~u7c75e310-c3a2-80fd-8004-7cc641479aef":{
"~#shape":{
"~:y":436,
"~:rx":0,
"~:transform":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:rotation":0,
"~:grow-type":"~:fixed",
"~:hide-in-viewer":false,
"~:name":"Rectangle",
"~:width":126.00000000000006,
"~:type":"~:rect",
"~:points":[
{
"~#point":{
"~:x":266,
"~:y":436
}
},
{
"~#point":{
"~:x":392.00000000000006,
"~:y":436
}
},
{
"~#point":{
"~:x":392.00000000000006,
"~:y":570
}
},
{
"~#point":{
"~:x":266,
"~:y":570
}
}
],
"~:proportion-lock":false,
"~:transform-inverse":{
"~#matrix":{
"~:a":1.0,
"~:b":0.0,
"~:c":0.0,
"~:d":1.0,
"~:e":0.0,
"~:f":0.0
}
},
"~:id":"~u7c75e310-c3a2-80fd-8004-7cc641479aef",
"~:parent-id":"~u00000000-0000-0000-0000-000000000000",
"~:frame-id":"~u00000000-0000-0000-0000-000000000000",
"~:strokes":[
],
"~:x":266,
"~:proportion":1,
"~:selrect":{
"~#rect":{
"~:x":266,
"~:y":436,
"~:width":126.00000000000006,
"~:height":134,
"~:x1":266,
"~:y1":436,
"~:x2":392.00000000000006,
"~:y2":570
}
},
"~:fills":[
{
"~:fill-color":"#B1B2B5",
"~:fill-opacity":1
}
],
"~:flip-x":null,
"~:ry":0,
"~:height":134,
"~:flip-y":null
}
}
},
"~:id":"~u6191cd35-bb1f-81f7-8004-7cc63d087375",
"~:name":"Page 1"
}
},
"~:id":"~u6191cd35-bb1f-81f7-8004-7cc63d087374"
}
}

View File

@@ -0,0 +1,47 @@
{
"~#set":[
{
"~:name":"Testing library 1",
"~:revn":2,
"~:modified-at":"~m1717512948250",
"~:thumbnail-uri":"http://localhost:3000/assets/by-id/5ad7a7a7-c64e-4bb8-852d-15708d125905",
"~:id":"~uc1249a66-fce0-8175-8004-7433fe4be8bc",
"~:is-shared":true,
"~:project-id":"~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:created-at":"~m1717512934704",
"~:library-summary":{
"~:components":{
"~:count":0,
"~:sample":[
]
},
"~:media":{
"~:count":0,
"~:sample":[
]
},
"~:colors":{
"~:count":1,
"~:sample":[
{
"~:path":"",
"~:color":"#187cd5",
"~:name":"test-color",
"~:modified-at":"~m1717512945259",
"~:opacity":1,
"~:id":"~uc70224ec-c410-807b-8004-74340616cffb"
}
]
},
"~:typographies":{
"~:count":0,
"~:sample":[
]
}
}
}
]
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -14,12 +14,17 @@ export class MockWebSocketHelper extends EventTarget {
}
this.#mocks.get(url).dispatchEvent(new MessageEvent("message", { data }));
});
await page.exposeFunction("onMockWebSocketSpyClose", (url, code, reason) => {
if (!this.#mocks.has(url)) {
throw new Error(`WebSocket with URL ${url} not found`);
}
this.#mocks.get(url).dispatchEvent(new CloseEvent("close", { code, reason }));
});
await page.exposeFunction(
"onMockWebSocketSpyClose",
(url, code, reason) => {
if (!this.#mocks.has(url)) {
throw new Error(`WebSocket with URL ${url} not found`);
}
this.#mocks
.get(url)
.dispatchEvent(new CloseEvent("close", { code, reason }));
},
);
await page.addInitScript({ path: "playwright/scripts/MockWebSocket.js" });
}

View File

@@ -188,13 +188,18 @@ window.WebSocket = class MockWebSocket extends EventTarget {
mockClose(code, reason) {
this.#readyState = MockWebSocket.CLOSED;
this.dispatchEvent(new CloseEvent("close", { code: code || 1000, reason: reason || "" }));
this.dispatchEvent(
new CloseEvent("close", { code: code || 1000, reason: reason || "" }),
);
return this;
}
send(data) {
if (this.#readyState === MockWebSocket.CONNECTING) {
throw new DOMException("InvalidStateError", "MockWebSocket is not connected");
throw new DOMException(
"InvalidStateError",
"MockWebSocket is not connected",
);
}
if (this.#spyMessage) {
@@ -203,7 +208,12 @@ window.WebSocket = class MockWebSocket extends EventTarget {
}
close(code, reason) {
if (code && !Number.isInteger(code) && code !== 1000 && (code < 3000 || code > 4999)) {
if (
code &&
!Number.isInteger(code) &&
code !== 1000 &&
(code < 3000 || code > 4999)
) {
throw new DOMException("InvalidAccessError", "Invalid code");
}
@@ -214,7 +224,9 @@ window.WebSocket = class MockWebSocket extends EventTarget {
}
}
if ([MockWebSocket.CLOSED, MockWebSocket.CLOSING].includes(this.#readyState)) {
if (
[MockWebSocket.CLOSED, MockWebSocket.CLOSING].includes(this.#readyState)
) {
return;
}

View File

@@ -4,7 +4,9 @@ export class BasePage {
throw new TypeError("Invalid page argument. Must be a Playwright page.");
}
if (typeof path !== "string" && !(path instanceof RegExp)) {
throw new TypeError("Invalid path argument. Must be a string or a RegExp.");
throw new TypeError(
"Invalid path argument. Must be a string or a RegExp.",
);
}
const url = typeof path === "string" ? `**/api/rpc/command/${path}` : path;

View File

@@ -1,3 +1,4 @@
import { expect } from "@playwright/test";
import { BaseWebSocketPage } from "./BaseWebSocketPage";
export class DashboardPage extends BaseWebSocketPage {
@@ -6,10 +7,9 @@ export class DashboardPage extends BaseWebSocketPage {
await BaseWebSocketPage.mockRPC(
page,
"get-profile",
"logged-in-user/get-profile-logged-in-no-onboarding.json",
"get-teams",
"logged-in-user/get-teams-default.json",
);
await BaseWebSocketPage.mockRPC(page, "get-teams", "logged-in-user/get-teams-default.json");
await BaseWebSocketPage.mockRPC(
page,
"get-font-variants?team-id=*",
@@ -54,39 +54,215 @@ export class DashboardPage extends BaseWebSocketPage {
}
static anyTeamId = "c7ce0794-0992-8105-8004-38e630f40f6d";
static secondTeamId = "dd33ff88-f4e5-8033-8003-8096cc07bdf3";
static draftProjectId = "c7ce0794-0992-8105-8004-38e630f7920b";
constructor(page) {
super(page);
this.titleLabel = page.getByRole("heading", { name: "Projects" });
this.addProjectBtn = page.getByRole("button", { name: "+ NEW PROJECT" });
this.sidebar = page.getByTestId("dashboard-sidebar");
this.sidebarMenu = this.sidebar.getByRole("menu");
this.mainHeading = page
.getByTestId("dashboard-header")
.getByRole("heading", { level: 1 });
this.addProjectButton = page.getByRole("button", { name: "+ NEW PROJECT" });
this.projectName = page.getByText("Project 1");
this.draftTitle = page.getByRole("heading", { name: "Drafts" });
this.draftLink = page.getByTestId("drafts-link-sidebar");
this.draftsFile = page.getByText(/New File 1/);
this.draftsLink = this.sidebar.getByText("Drafts");
this.fontsLink = this.sidebar.getByText("Fonts");
this.libsLink = this.sidebar.getByText("Libraries");
this.searchButton = page.getByRole("button", { name: "dashboard-search" });
this.searchInput = page.getByPlaceholder("Search…");
this.teamDropdown = this.sidebar.getByRole("button", {
name: "Your Penpot",
});
this.userAccount = this.sidebar.getByRole("button", {
name: /Princesa Leia/,
});
this.userProfileOption = this.sidebarMenu.getByText("Your account");
}
async setupDraftsEmpty() {
await this.mockRPC("get-project-files?project-id=*", "dashboard/get-project-files-empty.json");
await this.mockRPC(
"get-project-files?project-id=*",
"dashboard/get-project-files-empty.json",
);
}
async setupSearchEmpty() {
await this.mockRPC("search-files", "dashboard/search-files-empty.json", {
method: "POST",
});
}
async setupLibrariesEmpty() {
await this.mockRPC(
"get-team-shared-files?team-id=*",
"dashboard/get-shared-files-empty.json",
);
}
async setupDrafts() {
await this.mockRPC("get-project-files?project-id=*", "dashboard/get-project-files.json");
await this.mockRPC(
"get-project-files?project-id=*",
"dashboard/get-project-files.json",
);
}
async setupNewProject() {
await this.mockRPC("create-project", "dashboard/create-project.json", { method: "POST" });
await this.mockRPC("get-projects?team-id=*", "dashboard/get-projects-new.json");
await this.mockRPC("create-project", "dashboard/create-project.json", {
method: "POST",
});
await this.mockRPC(
"get-projects?team-id=*",
"dashboard/get-projects-new.json",
);
}
async goToWorkspace() {
await this.page.goto(`#/dashboard/team/${DashboardPage.anyTeamId}/projects`);
async setupDashboardFull() {
await this.mockRPC(
"get-projects?team-id=*",
"dashboard/get-projects-full.json",
);
await this.mockRPC(
"get-project-files?project-id=*",
"dashboard/get-project-files.json",
);
await this.mockRPC(
"get-team-shared-files?team-id=*",
"dashboard/get-shared-files.json",
);
await this.mockRPC(
"get-team-shared-files?project-id=*",
"dashboard/get-shared-files.json",
);
await this.mockRPC(
"get-team-recent-files?team-id=*",
"dashboard/get-team-recent-files.json",
);
await this.mockRPC(
"get-font-variants?team-id=*",
"dashboard/get-font-variants.json",
);
await this.mockRPC("search-files", "dashboard/search-files.json", {
method: "POST",
});
await this.mockRPC("search-files", "dashboard/search-files.json");
await this.mockRPC("get-teams", "logged-in-user/get-teams-complete.json");
}
async setupAccessTokensEmpty() {
await this.mockRPC(
"get-access-tokens",
"dashboard/get-access-tokens-empty.json",
);
}
async createAccessToken() {
await this.mockRPC(
"create-access-token",
"dashboard/create-access-token.json",
{ method: "POST" },
);
}
async setupAccessTokens() {
await this.mockRPC("get-access-tokens", "dashboard/get-access-tokens.json");
}
async setupTeamInvitationsEmpty() {
await this.mockRPC(
"get-team-invitations?team-id=*",
"dashboard/get-team-invitations-empty.json",
);
}
async setupTeamInvitations() {
await this.mockRPC(
"get-team-invitations?team-id=*",
"dashboard/get-team-invitations.json",
);
}
async setupTeamWebhooksEmpty() {
await this.mockRPC(
"get-webhooks?team-id=*",
"dashboard/get-webhooks-empty.json",
);
}
async setupTeamWebhooks() {
await this.mockRPC("get-webhooks?team-id=*", "dashboard/get-webhooks.json");
}
async setupTeamSettings() {
await this.mockRPC(
"get-team-stats?team-id=*",
"dashboard/get-team-stats.json",
);
}
async goToDashboard() {
await this.page.goto(
`#/dashboard/team/${DashboardPage.anyTeamId}/projects`,
);
await expect(this.mainHeading).toBeVisible();
}
async goToSecondTeamDashboard() {
await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/projects`,
);
}
async goToSecondTeamMembersSection() {
await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/members`,
);
}
async goToSecondTeamInvitationsSection() {
await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/invitations`,
);
}
async goToSecondTeamWebhooksSection() {
await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/webhooks`,
);
}
async goToSecondTeamWebhooksSection() {
await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/webhooks`,
);
}
async goToSecondTeamSettingsSection() {
await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/settings`,
);
}
async goToSearch() {
await this.page.goto(`#/dashboard/team/${DashboardPage.anyTeamId}/search`);
}
async goToDrafts() {
await this.page.goto(
`#/dashboard/team/${DashboardPage.anyTeamId}/projects/${DashboardPage.draftProjectId}`,
);
await expect(this.mainHeading).toHaveText("Drafts");
}
async goToAccount() {
await this.userAccount.click();
await this.userProfileOption.click();
}
}

View File

@@ -1,18 +1,18 @@
import { BasePage } from "./BasePage";
export class LoginPage extends BasePage {
static async initWithLoggedOutUser(page) {
await BasePage.mockRPC(page, "get-profile", "get-profile-anonymous.json");
}
constructor(page) {
super(page);
this.loginButton = page.getByRole("button", { name: "Login" });
this.password = page.getByLabel("Password");
this.userName = page.getByLabel("Email");
this.invalidCredentialsError = page.getByText("Email or password is incorrect");
this.invalidCredentialsError = page.getByText(
"Email or password is incorrect",
);
this.invalidEmailError = page.getByText("Enter a valid email please");
this.initialHeading = page.getByRole("heading", { name: "Log into my account" });
this.initialHeading = page.getByRole("heading", {
name: "Log into my account",
});
}
async fillEmailAndPasswordInputs(email, password) {
@@ -24,18 +24,40 @@ export class LoginPage extends BasePage {
await this.loginButton.click();
}
async initWithLoggedOutUser() {
await this.mockRPC("get-profile", "get-profile-anonymous.json");
}
async setupLoggedInUser() {
await this.mockRPC("get-profile", "logged-in-user/get-profile-logged-in.json");
await this.mockRPC(
"get-profile",
"logged-in-user/get-profile-logged-in.json",
);
await this.mockRPC("get-teams", "logged-in-user/get-teams-default.json");
await this.mockRPC("get-font-variants?team-id=*", "logged-in-user/get-font-variants-empty.json");
await this.mockRPC("get-projects?team-id=*", "logged-in-user/get-projects-default.json");
await this.mockRPC("get-team-members?team-id=*", "logged-in-user/get-team-members-your-penpot.json");
await this.mockRPC("get-team-users?team-id=*", "logged-in-user/get-team-users-single-user.json");
await this.mockRPC(
"get-font-variants?team-id=*",
"logged-in-user/get-font-variants-empty.json",
);
await this.mockRPC(
"get-projects?team-id=*",
"logged-in-user/get-projects-default.json",
);
await this.mockRPC(
"get-team-members?team-id=*",
"logged-in-user/get-team-members-your-penpot.json",
);
await this.mockRPC(
"get-team-users?team-id=*",
"logged-in-user/get-team-users-single-user.json",
);
await this.mockRPC(
"get-unread-comment-threads?team-id=*",
"logged-in-user/get-team-users-single-user.json",
);
await this.mockRPC("get-team-recent-files?team-id=*", "logged-in-user/get-team-recent-files-empty.json");
await this.mockRPC(
"get-team-recent-files?team-id=*",
"logged-in-user/get-team-recent-files-empty.json",
);
await this.mockRPC(
"get-profiles-for-file-comments",
"logged-in-user/get-profiles-for-file-comments-empty.json",
@@ -43,11 +65,18 @@ export class LoginPage extends BasePage {
}
async setupLoginSuccess() {
await this.mockRPC("login-with-password", "logged-in-user/login-with-password-success.json");
await this.mockRPC(
"login-with-password",
"logged-in-user/login-with-password-success.json",
);
}
async setupLoginError() {
await this.mockRPC("login-with-password", "login-with-password-error.json", { status: 400 });
await this.mockRPC(
"login-with-password",
"login-with-password-error.json",
{ status: 400 },
);
}
}

View File

@@ -0,0 +1,45 @@
import { BaseWebSocketPage } from "./BaseWebSocketPage";
export class OnboardingPage extends BaseWebSocketPage {
constructor(page) {
super(page);
this.submitButton = page.getByRole("Button", { name: "Next" });
}
async fillOnboardingInputsStep1() {
await this.page.getByText("Personal").click();
await this.page.getByText("Select option").click();
await this.page.getByText("Testing before self-hosting").click();
await this.submitButton.click();
}
async fillOnboardingInputsStep2() {
await this.page.getByText("Figma").click();
await this.submitButton.click();
}
async fillOnboardingInputsStep3() {
await this.page.getByText("Select option").first().click();
await this.page.getByText("Product Managment").click();
await this.page.getByText("Select option").first().click();
await this.page.getByText("Director").click();
await this.page.getByText("Select option").click();
await this.page.getByText("11-30").click();
await this.submitButton.click();
}
async fillOnboardingInputsStep4() {
await this.page.getByText("Other").click();
await this.page.getByPlaceholder("Other (specify)").fill("Another");
await this.submitButton.click();
}
async fillOnboardingInputsStep5() {
await this.page.getByText("Event").click();
}
}
export default OnboardingPage;

View File

@@ -0,0 +1,117 @@
import { BaseWebSocketPage } from "./BaseWebSocketPage";
export class ViewerPage extends BaseWebSocketPage {
static anyFileId = "c7ce0794-0992-8105-8004-38f280443849";
static anyPageId = "c7ce0794-0992-8105-8004-38f28044384a";
/**
* This should be called on `test.beforeEach`.
*
* @param {Page} page
* @returns
*/
static async init(page) {
await BaseWebSocketPage.initWebSockets(page);
}
async setupLoggedInUser() {
await this.mockRPC(
"get-profile",
"logged-in-user/get-profile-logged-in.json",
);
}
async setupEmptyFile() {
await this.mockRPC(
/get\-view\-only\-bundle\?/,
"viewer/get-view-only-bundle-empty-file.json",
);
await this.mockRPC(
"get-comment-threads?file-id=*",
"workspace/get-comment-threads-empty.json",
);
await this.mockRPC(
"get-file-fragment?file-id=*&fragment-id=*",
"viewer/get-file-fragment-empty-file.json",
);
}
async setupFileWithSingleBoard() {
await this.mockRPC(
/get\-view\-only\-bundle\?/,
"viewer/get-view-only-bundle-single-board.json",
);
await this.mockRPC(
"get-comment-threads?file-id=*",
"workspace/get-comment-threads-empty.json",
);
await this.mockRPC(
"get-file-fragment?file-id=*&fragment-id=*",
"viewer/get-file-fragment-single-board.json",
);
}
async setupFileWithComments() {
await this.mockRPC(
/get\-view\-only\-bundle\?/,
"viewer/get-view-only-bundle-single-board.json",
);
await this.mockRPC(
"get-comment-threads?file-id=*",
"workspace/get-comment-threads-not-empty.json",
);
await this.mockRPC(
"get-file-fragment?file-id=*&fragment-id=*",
"viewer/get-file-fragment-single-board.json",
);
await this.mockRPC(
"get-comments?thread-id=*",
"workspace/get-thread-comments.json",
);
await this.mockRPC(
"update-comment-thread-status",
"workspace/update-comment-thread-status.json",
);
}
#ws = null;
constructor(page) {
super(page);
}
async goToViewer({
fileId = ViewerPage.anyFileId,
pageId = ViewerPage.anyPageId,
} = {}) {
await this.page.goto(
`/#/view/${fileId}?page-id=${pageId}&section=interactions&index=0`,
);
this.#ws = await this.waitForNotificationsWebSocket();
await this.#ws.mockOpen();
}
async cleanUp() {
await this.#ws.mockClose();
}
async showComments(clickOptions = {}) {
await this.page
.getByRole("button", { name: "Comments (G C)" })
.click(clickOptions);
}
async showCommentsThread(number, clickOptions = {}) {
await this.page
.getByTestId("floating-thread-bubble")
.filter({ hasText: number.toString() })
.click(clickOptions);
}
async showCode(clickOptions = {}) {
await this.page
.getByRole("button", { name: "Inspect (G I)" })
.click(clickOptions);
}
}

View File

@@ -11,7 +11,11 @@ export class WorkspacePage extends BaseWebSocketPage {
static async init(page) {
await BaseWebSocketPage.initWebSockets(page);
await BaseWebSocketPage.mockRPC(page, "get-profile", "logged-in-user/get-profile-logged-in.json");
await BaseWebSocketPage.mockRPC(
page,
"get-profile",
"logged-in-user/get-profile-logged-in.json",
);
await BaseWebSocketPage.mockRPC(
page,
"get-team-users?file-id=*",
@@ -22,8 +26,16 @@ export class WorkspacePage extends BaseWebSocketPage {
"get-comment-threads?file-id=*",
"workspace/get-comment-threads-empty.json",
);
await BaseWebSocketPage.mockRPC(page, "get-project?id=*", "workspace/get-project-default.json");
await BaseWebSocketPage.mockRPC(page, "get-team?id=*", "workspace/get-team-default.json");
await BaseWebSocketPage.mockRPC(
page,
"get-project?id=*",
"workspace/get-project-default.json",
);
await BaseWebSocketPage.mockRPC(
page,
"get-team?id=*",
"workspace/get-team-default.json",
);
await BaseWebSocketPage.mockRPC(
page,
"get-profiles-for-file-comments?file-id=*",
@@ -40,16 +52,37 @@ export class WorkspacePage extends BaseWebSocketPage {
constructor(page) {
super(page);
this.pageName = page.getByTestId("page-name");
this.presentUserListItems = page.getByTestId("active-users-list").getByAltText("Princesa Leia");
this.presentUserListItems = page
.getByTestId("active-users-list")
.getByAltText("Princesa Leia");
this.viewport = page.getByTestId("viewport");
this.rootShape = page.locator(`[id="shape-00000000-0000-0000-0000-000000000000"]`);
this.rootShape = page.locator(
`[id="shape-00000000-0000-0000-0000-000000000000"]`,
);
this.toolbarOptions = page.getByTestId("toolbar-options");
this.rectShapeButton = page.getByRole("button", { name: "Rectangle (R)" });
this.toggleToolbarButton = page.getByRole("button", {
name: "Toggle toolbar",
});
this.colorpicker = page.getByTestId("colorpicker");
this.layers = page.getByTestId("layer-tree");
this.palette = page.getByTestId("palette");
this.sidebar = page.getByTestId("left-sidebar");
this.rightSidebar = page.getByTestId("right-sidebar");
this.selectionRect = page.getByTestId("workspace-selection-rect");
this.horizontalScrollbar = page.getByTestId("horizontal-scrollbar");
this.librariesModal = page.getByTestId("libraries-modal");
this.togglePalettesVisibility = page.getByTestId(
"toggle-palettes-visibility",
);
}
async goToWorkspace() {
async goToWorkspace({
fileId = WorkspacePage.anyFileId,
pageId = WorkspacePage.anyPageId,
} = {}) {
await this.page.goto(
`/#/workspace/${WorkspacePage.anyProjectId}/${WorkspacePage.anyFileId}?page-id=${WorkspacePage.anyPageId}`,
`/#/workspace/${WorkspacePage.anyProjectId}/${fileId}?page-id=${pageId}`,
);
this.#ws = await this.waitForNotificationsWebSocket();
@@ -71,10 +104,22 @@ export class WorkspacePage extends BaseWebSocketPage {
}
async setupEmptyFile() {
await this.mockRPC("get-profile", "logged-in-user/get-profile-logged-in.json");
await this.mockRPC("get-team-users?file-id=*", "logged-in-user/get-team-users-single-user.json");
await this.mockRPC("get-comment-threads?file-id=*", "workspace/get-comment-threads-empty.json");
await this.mockRPC("get-project?id=*", "workspace/get-project-default.json");
await this.mockRPC(
"get-profile",
"logged-in-user/get-profile-logged-in.json",
);
await this.mockRPC(
"get-team-users?file-id=*",
"logged-in-user/get-team-users-single-user.json",
);
await this.mockRPC(
"get-comment-threads?file-id=*",
"workspace/get-comment-threads-empty.json",
);
await this.mockRPC(
"get-project?id=*",
"workspace/get-project-default.json",
);
await this.mockRPC("get-team?id=*", "workspace/get-team-default.json");
await this.mockRPC(
"get-profiles-for-file-comments?file-id=*",
@@ -85,9 +130,18 @@ export class WorkspacePage extends BaseWebSocketPage {
"get-file-object-thumbnails?file-id=*",
"workspace/get-file-object-thumbnails-blank.json",
);
await this.mockRPC("get-font-variants?team-id=*", "workspace/get-font-variants-empty.json");
await this.mockRPC("get-file-fragment?file-id=*", "workspace/get-file-fragment-blank.json");
await this.mockRPC("get-file-libraries?file-id=*", "workspace/get-file-libraries-empty.json");
await this.mockRPC(
"get-font-variants?team-id=*",
"workspace/get-font-variants-empty.json",
);
await this.mockRPC(
"get-file-fragment?file-id=*",
"workspace/get-file-fragment-blank.json",
);
await this.mockRPC(
"get-file-libraries?file-id=*",
"workspace/get-file-libraries-empty.json",
);
}
async clickWithDragViewportAt(x, y, width, height) {
@@ -97,4 +151,87 @@ export class WorkspacePage extends BaseWebSocketPage {
await this.viewport.hover({ position: { x: x + width, y: y + height } });
await this.page.mouse.up();
}
async panOnViewportAt(x, y, width, height) {
await this.page.waitForTimeout(100);
await this.viewport.hover({ position: { x, y } });
await this.page.mouse.down({ button: "middle" });
await this.viewport.hover({ position: { x: x + width, y: y + height } });
await this.page.mouse.up({ button: "middle" });
}
async togglePages() {
const pagesToggle = this.page.getByText("Pages");
await pagesToggle.click();
}
async moveSelectionToShape(name) {
await this.page.locator("rect.viewport-selrect").hover();
await this.page.mouse.down();
await this.viewport.getByTestId(name).first().hover({ force: true });
await this.page.mouse.up();
}
async clickLeafLayer(name, clickOptions = {}) {
const layer = this.layers.getByText(name);
await layer.click(clickOptions);
}
async clickToggableLayer(name, clickOptions = {}) {
const layer = this.layers
.getByTestId("layer-item")
.filter({ has: this.page.getByText(name) });
await layer.getByRole("button").click(clickOptions);
}
async expectSelectedLayer(name) {
await expect(
this.layers
.getByTestId("layer-row")
.filter({ has: this.page.getByText(name) }),
).toHaveClass(/selected/);
}
async expectHiddenToolbarOptions() {
await expect(this.toolbarOptions).toHaveCSS("opacity", "0");
}
async clickAssets(clickOptions = {}) {
await this.sidebar.getByText("Assets").click(clickOptions);
}
async openLibrariesModal(clickOptions = {}) {
await this.sidebar.getByText("Libraries").click(clickOptions);
await expect(this.librariesModal).toBeVisible();
}
async clickLibrary(name, clickOptions = {}) {
await this.page
.getByTestId("library-item")
.filter({ hasText: name })
.getByRole("button")
.click(clickOptions);
}
async closeLibrariesModal(clickOptions = {}) {
await this.librariesModal
.getByRole("button", { name: "Close" })
.click(clickOptions);
}
async clickColorPalette(clickOptions = {}) {
await this.palette
.getByRole("button", { name: "Color Palette (Alt+P)" })
.click(clickOptions);
}
async clickColorPalette(clickOptions = {}) {
await this.palette
.getByRole("button", { name: "Color Palette (Alt+P)" })
.click(clickOptions);
}
async clickTogglePalettesVisibility(clickOptions = {}) {
await this.togglePalettesVisibility.click(clickOptions);
}
}

View File

@@ -6,7 +6,9 @@ test.beforeEach(async ({ page }) => {
});
// Fix for https://tree.taiga.io/project/penpot/issue/7549
test("Bug 7549 - User clicks on color swatch to display the color picker next to it", async ({ page }) => {
test("Bug 7549 - User clicks on color swatch to display the color picker next to it", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile(page);

View File

@@ -3,23 +3,28 @@ import DashboardPage from "../pages/DashboardPage";
test.beforeEach(async ({ page }) => {
await DashboardPage.init(page);
await DashboardPage.mockRPC(
page,
"get-profile",
"logged-in-user/get-profile-logged-in-no-onboarding.json",
);
});
test("Dashboad page has title ", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToWorkspace();
await dashboardPage.goToDashboard();
await expect(dashboardPage.page).toHaveURL(/dashboard/);
await expect(dashboardPage.titleLabel).toBeVisible();
await expect(dashboardPage.mainHeading).toBeVisible();
});
test("User can create a new project", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupNewProject();
await dashboardPage.goToWorkspace();
await dashboardPage.addProjectBtn.click();
await dashboardPage.goToDashboard();
await dashboardPage.addProjectButton.click();
await expect(dashboardPage.projectName).toBeVisible();
});
@@ -28,17 +33,22 @@ test("User goes to draft page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDraftsEmpty();
await dashboardPage.goToWorkspace();
await dashboardPage.draftLink.click();
await dashboardPage.goToDashboard();
await dashboardPage.draftsLink.click();
await expect(dashboardPage.draftTitle).toBeVisible();
await expect(dashboardPage.mainHeading).toHaveText("Drafts");
});
test("User loads the draft page", async ({ page }) => {
test("Lists files in the drafts page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDrafts();
await dashboardPage.goToDrafts();
await expect(dashboardPage.draftsFile).toBeVisible();
await expect(
dashboardPage.page.getByRole("button", { name: /New File 1/ }),
).toBeVisible();
await expect(
dashboardPage.page.getByRole("button", { name: /New File 2/ }),
).toBeVisible();
});

View File

@@ -0,0 +1,145 @@
import { test, expect } from "@playwright/test";
import { WorkspacePage } from "../pages/WorkspacePage";
test.beforeEach(async ({ page }) => {
await WorkspacePage.init(page);
});
const multipleConstraintsFileId = `03bff843-920f-81a1-8004-756365e1eb6a`;
const multipleConstraintsPageId = `03bff843-920f-81a1-8004-756365e1eb6b`;
const multipleAttributesFileId = `1795a568-0df0-8095-8004-7ba741f56be2`;
const multipleAttributesPageId = `1795a568-0df0-8095-8004-7ba741f56be3`;
const setupFileWithMultipeConstraints = async (workspace) => {
await workspace.setupEmptyFile();
await workspace.mockRPC(
/get\-file\?/,
"design/get-file-multiple-constraints.json",
);
await workspace.mockRPC(
"get-file-object-thumbnails?file-id=*",
"design/get-file-object-thumbnails-multiple-constraints.json",
);
await workspace.mockRPC(
"get-file-fragment?file-id=*",
"design/get-file-fragment-multiple-constraints.json",
);
};
const setupFileWithMultipeAttributes = async (workspace) => {
await workspace.setupEmptyFile();
await workspace.mockRPC(
/get\-file\?/,
"design/get-file-multiple-attributes.json",
);
await workspace.mockRPC(
"get-file-object-thumbnails?file-id=*",
"design/get-file-object-thumbnails-multiple-attributes.json",
);
};
test.describe("Constraints", () => {
test("Constraint dropdown shows 'Mixed' when multiple layers are selected with different constraints", async ({
page,
}) => {
const workspace = new WorkspacePage(page);
await setupFileWithMultipeConstraints(workspace);
await workspace.goToWorkspace({
fileId: multipleConstraintsFileId,
pageId: multipleConstraintsPageId,
});
await workspace.clickToggableLayer("Board");
await workspace.clickLeafLayer("Ellipse");
await workspace.clickLeafLayer("Rectangle", { modifiers: ["Shift"] });
const constraintVDropdown = workspace.page.getByTestId(
"constraint-v-select",
);
await expect(constraintVDropdown).toContainText("Mixed");
const constraintHDropdown = workspace.page.getByTestId(
"constraint-h-select",
);
await expect(constraintHDropdown).toContainText("Mixed");
expect(false);
});
});
test.describe("Multiple shapes attributes", () => {
test("User selects multiple shapes with sames fills, strokes, shadows and blur", async ({
page,
}) => {
const workspace = new WorkspacePage(page);
await setupFileWithMultipeConstraints(workspace);
await workspace.goToWorkspace({
fileId: multipleConstraintsFileId,
pageId: multipleConstraintsPageId,
});
await workspace.clickToggableLayer("Board");
await workspace.clickLeafLayer("Ellipse");
await workspace.clickLeafLayer("Rectangle", { modifiers: ["Shift"] });
await expect(workspace.page.getByTestId("add-fill")).toBeVisible();
await expect(workspace.page.getByTestId("add-stroke")).toBeVisible();
await expect(workspace.page.getByTestId("add-shadow")).toBeVisible();
await expect(workspace.page.getByTestId("add-blur")).toBeVisible();
});
test("User selects multiple shapes with different fills, strokes, shadows and blur", async ({
page,
}) => {
const workspace = new WorkspacePage(page);
await setupFileWithMultipeAttributes(workspace);
await workspace.goToWorkspace({
fileId: multipleAttributesFileId,
pageId: multipleAttributesPageId,
});
await workspace.clickLeafLayer("Ellipse");
await workspace.clickLeafLayer("Rectangle", { modifiers: ["Shift"] });
await expect(workspace.page.getByTestId("add-fill")).toBeHidden();
await expect(workspace.page.getByTestId("add-stroke")).toBeHidden();
await expect(workspace.page.getByTestId("add-shadow")).toBeHidden();
await expect(workspace.page.getByTestId("add-blur")).toBeHidden();
});
});
test("BUG 7760 - Layout losing properties when changing parents", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-7760.json");
await workspacePage.mockRPC(
"get-file-fragment?file-id=*&fragment-id=*",
"workspace/get-file-fragment-7760.json",
);
await workspacePage.mockRPC(
"update-file?id=*",
"workspace/update-file-create-rect.json",
);
await workspacePage.goToWorkspace({
fileId: "cd90e028-326a-80b4-8004-7cdec16ffad5",
pageId: "cd90e028-326a-80b4-8004-7cdec16ffad6",
});
// Select the flex board and drag it into the other container board
await workspacePage.clickLeafLayer("Flex Board");
// Move the first board into the second
const hAuto = await workspacePage.page.getByTitle("Fit content (Horizontal)");
const vAuto = await workspacePage.page.getByTitle("Fit content (Vertical)");
await expect(vAuto.locator("input")).toBeChecked();
await expect(hAuto.locator("input")).toBeChecked();
await workspacePage.moveSelectionToShape("Container Board");
// The first board properties should still be auto width/height
await expect(vAuto.locator("input")).toBeChecked();
await expect(hAuto.locator("input")).toBeChecked();
});

View File

@@ -2,11 +2,15 @@ import { test, expect } from "@playwright/test";
import { LoginPage } from "../pages/LoginPage";
test.beforeEach(async ({ page }) => {
await LoginPage.initWithLoggedOutUser(page);
const login = new LoginPage(page);
await login.initWithLoggedOutUser();
await page.goto("/#/auth/login");
});
test("User is redirected to the login page when logged out", async ({ page }) => {
test("User is redirected to the login page when logged out", async ({
page,
}) => {
const loginPage = new LoginPage(page);
await loginPage.setupLoggedInUser();
@@ -28,7 +32,9 @@ test.describe("Login form", () => {
await expect(loginPage.page).toHaveURL(/dashboard/);
});
test("User gets error message when submitting an bad formatted email ", async ({ page }) => {
test("User gets error message when submitting an bad formatted email ", async ({
page,
}) => {
const loginPage = new LoginPage(page);
await loginPage.setupLoginSuccess();
@@ -37,11 +43,16 @@ test.describe("Login form", () => {
await expect(loginPage.invalidEmailError).toBeVisible();
});
test("User gets error message when submitting wrong credentials", async ({ page }) => {
test("User gets error message when submitting wrong credentials", async ({
page,
}) => {
const loginPage = new LoginPage(page);
await loginPage.setupLoginError();
await loginPage.fillEmailAndPasswordInputs("test@example.com", "loremipsum");
await loginPage.fillEmailAndPasswordInputs(
"test@example.com",
"loremipsum",
);
await loginPage.clickLoginButton();
await expect(loginPage.invalidCredentialsError).toBeVisible();

View File

@@ -0,0 +1,45 @@
import { test, expect } from "@playwright/test";
import DashboardPage from "../pages/DashboardPage";
import OnboardingPage from "../pages/OnboardingPage";
test.beforeEach(async ({ page }) => {
await DashboardPage.init(page);
await DashboardPage.mockRPC(
page,
"get-profile",
"logged-in-user/get-profile-logged-in.json",
);
});
test("User can complete the onboarding", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
const onboardingPage = new OnboardingPage(page);
await dashboardPage.goToDashboard();
await expect(
page.getByRole("heading", { name: "Help us get to know you" }),
).toBeVisible();
await onboardingPage.fillOnboardingInputsStep1();
await expect(
page.getByRole("heading", { name: "Which one of these tools do" }),
).toBeVisible();
await onboardingPage.fillOnboardingInputsStep2();
await expect(
page.getByRole("heading", { name: "Tell us about your job" }),
).toBeVisible();
await onboardingPage.fillOnboardingInputsStep3();
await expect(
page.getByRole("heading", { name: "Where would you like to get" }),
).toBeVisible();
await onboardingPage.fillOnboardingInputsStep4();
await expect(
page.getByRole("heading", { name: "How did you hear about Penpot?" }),
).toBeVisible();
await onboardingPage.fillOnboardingInputsStep5();
await expect(page.getByRole("button", { name: "Start" })).toBeEnabled();
});

View File

@@ -0,0 +1,73 @@
import { test, expect } from "@playwright/test";
import { WorkspacePage } from "../pages/WorkspacePage";
test.beforeEach(async ({ page }) => {
await WorkspacePage.init(page);
});
test.describe("Layers tab", () => {
test("BUG 7466 - Layers tab height extends to the bottom when 'Pages' is collapsed", async ({
page,
}) => {
const workspace = new WorkspacePage(page);
await workspace.setupEmptyFile();
await workspace.goToWorkspace();
const { height: heightExpanded } = await workspace.layers.boundingBox();
await workspace.togglePages();
const { height: heightCollapsed } = await workspace.layers.boundingBox();
expect(heightExpanded > heightCollapsed);
});
});
test.describe("Assets tab", () => {
test("User adds a library and its automatically selected in the color palette", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC(
"link-file-to-library",
"workspace/link-file-to-library.json",
);
await workspacePage.mockRPC(
"unlink-file-from-library",
"workspace/unlink-file-from-library.json",
);
await workspacePage.mockRPC(
"get-team-shared-files?team-id=*",
"workspace/get-team-shared-libraries-non-empty.json",
);
await workspacePage.goToWorkspace();
// Add Testing library 1
await workspacePage.clickColorPalette();
await workspacePage.clickAssets();
// Now the get-file call should return a library
await workspacePage.mockRPC(
/get\-file\?/,
"workspace/get-file-library.json",
);
await workspacePage.openLibrariesModal();
await workspacePage.clickLibrary("Testing library 1");
await workspacePage.closeLibrariesModal();
await expect(
workspacePage.palette.getByRole("button", { name: "test-color-187cd5" }),
).toBeVisible();
// Remove Testing library 1
await workspacePage.openLibrariesModal();
await workspacePage.clickLibrary("Testing library 1");
await workspacePage.closeLibrariesModal();
await expect(
workspacePage.palette.getByText(
"There are no color styles in your library yet",
),
).toBeVisible();
});
});

View File

@@ -0,0 +1,30 @@
import { test, expect } from "@playwright/test";
import { ViewerPage } from "../pages/ViewerPage";
test.beforeEach(async ({ page }) => {
await ViewerPage.init(page);
});
const singleBoardFileId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb1";
const singleBoardPageId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb2";
test("Comment is shown with scroll and valid position", async ({ page }) => {
const viewer = new ViewerPage(page);
await viewer.setupLoggedInUser();
await viewer.setupFileWithComments();
await viewer.goToViewer({
fileId: singleBoardFileId,
pageId: singleBoardPageId,
});
await viewer.showComments();
await viewer.showCommentsThread(1);
await expect(
viewer.page.getByRole("textbox", { name: "Reply" }),
).toBeVisible();
await viewer.showCommentsThread(1);
await viewer.showCommentsThread(2);
await expect(
viewer.page.getByRole("textbox", { name: "Reply" }),
).toBeVisible();
});

View File

@@ -0,0 +1,42 @@
import { test, expect } from "@playwright/test";
import { ViewerPage } from "../pages/ViewerPage";
test.beforeEach(async ({ page }) => {
await ViewerPage.init(page);
});
const singleBoardFileId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb1";
const singleBoardPageId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb2";
test("Clips link area of the logo", async ({ page }) => {
const viewerPage = new ViewerPage(page);
await viewerPage.setupLoggedInUser();
await viewerPage.setupEmptyFile();
await viewerPage.goToViewer();
const viewerUrl = page.url();
const logoLink = viewerPage.page.getByTestId("penpot-logo-link");
await expect(logoLink).toBeVisible();
const { x, y } = await logoLink.boundingBox();
await viewerPage.page.mouse.click(x, y + 100);
await expect(page.url()).toBe(viewerUrl);
});
test("Updates URL with zoom type", async ({ page }) => {
const viewer = new ViewerPage(page);
await viewer.setupLoggedInUser();
await viewer.setupFileWithSingleBoard(viewer);
await viewer.goToViewer({
fileId: singleBoardFileId,
pageId: singleBoardPageId,
});
await viewer.page.getByTitle("Zoom").click();
await viewer.page.getByText(/Fit/).click();
await expect(viewer.page).toHaveURL(/&zoom=fit/);
});

View File

@@ -15,20 +15,27 @@ test("User loads worskpace with empty file", async ({ page }) => {
await expect(workspacePage.pageName).toHaveText("Page 1");
});
test("User receives presence notifications updates in the workspace", async ({ page }) => {
test("User receives presence notifications updates in the workspace", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.goToWorkspace();
await workspacePage.sendPresenceMessage(presenceFixture);
await expect(page.getByTestId("active-users-list").getByAltText("Princesa Leia")).toHaveCount(2);
await expect(
page.getByTestId("active-users-list").getByAltText("Princesa Leia"),
).toHaveCount(2);
});
test("User draws a rect", async ({ page }) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC("update-file?id=*", "workspace/update-file-create-rect.json");
await workspacePage.mockRPC(
"update-file?id=*",
"workspace/update-file-create-rect.json",
);
await workspacePage.goToWorkspace();
await workspacePage.rectShapeButton.click();
@@ -38,3 +45,135 @@ test("User draws a rect", async ({ page }) => {
await expect(shape).toHaveAttribute("width", "200");
await expect(shape).toHaveAttribute("height", "100");
});
test("User makes a group", async ({ page }) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC(
/get\-file\?/,
"workspace/get-file-not-empty.json",
);
await workspacePage.mockRPC(
"update-file?id=*",
"workspace/update-file-create-rect.json",
);
await workspacePage.goToWorkspace({
fileId: "6191cd35-bb1f-81f7-8004-7cc63d087374",
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
});
await workspacePage.clickLeafLayer("Rectangle");
await workspacePage.page.keyboard.press("Control+g");
await workspacePage.expectSelectedLayer("Group");
});
test("Bug 7654 - Toolbar keeps toggling on and off on spacebar press", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.goToWorkspace();
await workspacePage.toggleToolbarButton.click();
await workspacePage.page.keyboard.press("Backspace");
await workspacePage.page.keyboard.press("Enter");
await workspacePage.expectHiddenToolbarOptions();
});
test("Bug 7525 - User moves a scrollbar and no selciont rectangle appears", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC(
/get\-file\?/,
"workspace/get-file-not-empty.json",
);
await workspacePage.mockRPC(
"update-file?id=*",
"workspace/update-file-create-rect.json",
);
await workspacePage.goToWorkspace({
fileId: "6191cd35-bb1f-81f7-8004-7cc63d087374",
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
});
// Move created rect to a corner, in orther to get scrollbars
await workspacePage.panOnViewportAt(128, 128, 300, 300);
// Check scrollbars appear
const horizontalScrollbar = workspacePage.horizontalScrollbar;
await expect(horizontalScrollbar).toBeVisible();
// Grab scrollbar and move
const { x, y } = await horizontalScrollbar.boundingBox();
await page.waitForTimeout(100);
await workspacePage.viewport.hover({ position: { x: x, y: y + 5 } });
await page.mouse.down();
await workspacePage.viewport.hover({ position: { x: x - 130, y: y - 95 } });
await expect(workspacePage.selectionRect).not.toBeInViewport();
});
test("User adds a library and its automatically selected in the color palette", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC(
"link-file-to-library",
"workspace/link-file-to-library.json",
);
await workspacePage.mockRPC(
"unlink-file-from-library",
"workspace/unlink-file-from-library.json",
);
await workspacePage.mockRPC(
"get-team-shared-files?team-id=*",
"workspace/get-team-shared-libraries-non-empty.json",
);
await workspacePage.goToWorkspace();
// Add Testing library 1
await workspacePage.clickColorPalette();
await workspacePage.clickAssets();
// Now the get-file call should return a library
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-library.json");
await workspacePage.openLibrariesModal();
await workspacePage.clickLibrary("Testing library 1");
await workspacePage.closeLibrariesModal();
await expect(
workspacePage.palette.getByRole("button", { name: "test-color-187cd5" }),
).toBeVisible();
// Remove Testing library 1
await workspacePage.openLibrariesModal();
await workspacePage.clickLibrary("Testing library 1");
await workspacePage.closeLibrariesModal();
await expect(
workspacePage.palette.getByText(
"There are no color styles in your library yet",
),
).toBeVisible();
});
test("Bug 7489 - Workspace-palette items stay hidden when opening with keyboard-shortcut", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.goToWorkspace();
await workspacePage.clickTogglePalettesVisibility();
await workspacePage.page.keyboard.press("Alt+t");
await expect(
workspacePage.palette.getByText(
"There are no typography styles in your library yet",
),
).toBeVisible();
});

View File

@@ -1,10 +0,0 @@
import { test, expect } from "@playwright/test";
import { LoginPage } from "../pages/LoginPage";
test("Shows login form correctly", async ({ page }) => {
await LoginPage.initWithLoggedOutUser(page);
const loginPage = new LoginPage(page);
await page.goto("/#/auth/login");
await expect(page).toHaveScreenshot();
});

View File

@@ -0,0 +1,317 @@
import { test, expect } from "@playwright/test";
import DashboardPage from "../pages/DashboardPage";
test.beforeEach(async ({ page }) => {
await DashboardPage.init(page);
await DashboardPage.mockRPC(
page,
"get-profile",
"logged-in-user/get-profile-logged-in-no-onboarding.json",
);
});
test("User goes to an empty dashboard", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await expect(dashboardPage.mainHeading).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
// Empty dashboard pages
test("User goes to an empty draft page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDraftsEmpty();
await dashboardPage.goToDashboard();
await dashboardPage.draftsLink.click();
await expect(dashboardPage.mainHeading).toHaveText("Drafts");
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to an empty fonts page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await dashboardPage.fontsLink.click();
await expect(dashboardPage.mainHeading).toHaveText("Fonts");
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to an empty libraries page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupLibrariesEmpty();
await dashboardPage.goToDashboard();
await dashboardPage.libsLink.click();
await expect(dashboardPage.mainHeading).toHaveText("Libraries");
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to an empty search page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupSearchEmpty();
await dashboardPage.goToSearch();
await expect(dashboardPage.mainHeading).toHaveText("Search results");
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to the dashboard with a new project", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupNewProject();
await dashboardPage.goToDashboard();
await expect(dashboardPage.projectName).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
// Dashboard pages with content
test("User goes to a full dashboard", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.goToDashboard();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to a full draft page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.goToDashboard();
await dashboardPage.draftsLink.click();
await expect(dashboardPage.mainHeading).toHaveText("Drafts");
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to a full library page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.goToDashboard();
await dashboardPage.libsLink.click();
await expect(dashboardPage.mainHeading).toHaveText("Libraries");
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to a full fonts page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.goToDashboard();
await dashboardPage.fontsLink.click();
await expect(dashboardPage.mainHeading).toHaveText("Fonts");
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to a full search page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.goToSearch();
await expect(dashboardPage.searchInput).toBeVisible();
await dashboardPage.searchInput.fill("3");
await expect(dashboardPage.mainHeading).toHaveText("Search results");
await expect(
dashboardPage.page.getByRole("button", { name: "New File 3" }),
).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
// Account management
test("User opens user account", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await expect(dashboardPage.userAccount).toBeVisible();
await dashboardPage.goToAccount();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to user profile", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await dashboardPage.goToAccount();
await expect(dashboardPage.mainHeading).toHaveText("Your account");
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to password management section", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await dashboardPage.goToAccount();
await page.getByText("Password").click();
await expect(
page.getByRole("heading", { name: "Change Password" }),
).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to settings section", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await dashboardPage.goToAccount();
await page.getByTestId("settings-profile").click();
await expect(page.getByRole("heading", { name: "Settings" })).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
// Teams management
test("User opens teams selector with only one team", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await dashboardPage.teamDropdown.click();
await expect(page.getByText("Create new team")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User opens teams selector with more than one team", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.goToDashboard();
await dashboardPage.teamDropdown.click();
await expect(page.getByText("Second Team")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to second team", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.goToDashboard();
await dashboardPage.teamDropdown.click();
await expect(page.getByText("Second Team")).toBeVisible();
await page.getByText("Second Team").click();
await expect(page.getByText("Team Up")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User opens team management dropdown", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.goToSecondTeamDashboard();
await expect(page.getByText("Team Up")).toBeVisible();
await page.getByRole("button", { name: "team-management" }).click();
await expect(page.getByTestId("team-members")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to team management section", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.goToSecondTeamMembersSection();
await expect(page.getByText("role")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to an empty invitations section", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.setupTeamInvitationsEmpty();
await dashboardPage.goToSecondTeamInvitationsSection();
await expect(page.getByText("No pending invitations")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to a complete invitations section", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.setupTeamInvitations();
await dashboardPage.goToSecondTeamInvitationsSection();
await expect(page.getByText("test1@mail.com")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User invite people to the team", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.setupTeamInvitationsEmpty();
await dashboardPage.goToSecondTeamInvitationsSection();
await expect(page.getByTestId("invite-member")).toBeVisible();
await page.getByTestId("invite-member").click();
await expect(page.getByText("Invite with the role")).toBeVisible();
await page.getByPlaceholder("Emails, comma separated").fill("test5@mail.com");
await expect(page.getByText("Send invitation")).not.toBeDisabled();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to an empty webhook section", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.setupTeamWebhooksEmpty();
await dashboardPage.goToSecondTeamWebhooksSection();
await expect(page.getByText("No webhooks created so far.")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to a complete webhook section", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.setupTeamWebhooks();
await dashboardPage.goToSecondTeamWebhooksSection();
await expect(page.getByText("https://www.google.com")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});
test("User goes to the team settings section", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.setupTeamSettings();
await dashboardPage.goToSecondTeamSettingsSection();
await expect(page.getByText("TEAM INFO")).toBeVisible();
await expect(dashboardPage.page).toHaveScreenshot();
});

View File

@@ -0,0 +1,37 @@
import { test, expect } from "@playwright/test";
import { LoginPage } from "../pages/LoginPage";
test.beforeEach(async ({ page }) => {
const login = new LoginPage(page);
await login.initWithLoggedOutUser();
await login.page.goto("/#/auth/login");
});
test.describe("Login form", () => {
test("Shows the login form correctly", async ({ page }) => {
const login = new LoginPage(page);
await expect(login.page).toHaveScreenshot();
});
test("Shows form error messages correctly ", async ({ page }) => {
const login = new LoginPage(page);
await login.setupLoginSuccess();
await login.fillEmailAndPasswordInputs("foo", "lorenIpsum");
await expect(login.invalidEmailError).toBeVisible();
await expect(login.page).toHaveScreenshot();
});
test("Shows error toasts correctly", async ({ page }) => {
const login = new LoginPage(page);
await login.setupLoginError();
await login.fillEmailAndPasswordInputs("test@example.com", "loremipsum");
await login.clickLoginButton();
await expect(login.invalidCredentialsError).toBeVisible();
await expect(login.page).toHaveURL(/auth\/login$/);
await expect(login.page).toHaveScreenshot();
});
});

View File

@@ -0,0 +1,145 @@
import { test, expect } from "@playwright/test";
import { ViewerPage } from "../pages/ViewerPage";
test.beforeEach(async ({ page }) => {
await ViewerPage.init(page);
});
const singleBoardFileId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb1";
const singleBoardPageId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb2";
test("User goes to an empty Viewer", async ({ page }) => {
const viewerPage = new ViewerPage(page);
await viewerPage.setupLoggedInUser();
await viewerPage.setupEmptyFile();
await viewerPage.goToViewer();
await expect(viewerPage.page.getByTestId("penpot-logo-link")).toBeVisible();
await expect(viewerPage.page).toHaveScreenshot();
});
test("User goes to the Viewer", async ({ page }) => {
const viewerPage = new ViewerPage(page);
await viewerPage.setupLoggedInUser();
await viewerPage.setupFileWithSingleBoard();
await viewerPage.goToViewer({
fileId: singleBoardFileId,
pageId: singleBoardPageId,
});
await expect(viewerPage.page.getByTestId("penpot-logo-link")).toBeVisible();
await expect(viewerPage.page).toHaveScreenshot();
});
test("User goes to the Viewer and opens zoom modal", async ({ page }) => {
const viewerPage = new ViewerPage(page);
await viewerPage.setupLoggedInUser();
await viewerPage.setupFileWithSingleBoard();
await viewerPage.goToViewer({
fileId: singleBoardFileId,
pageId: singleBoardPageId,
});
await viewerPage.page.getByTitle("Zoom").click();
await expect(viewerPage.page.getByTestId("penpot-logo-link")).toBeVisible();
await expect(viewerPage.page).toHaveScreenshot();
});
test("User goes to the Viewer Comments", async ({ page }) => {
const viewerPage = new ViewerPage(page);
await viewerPage.setupLoggedInUser();
await viewerPage.setupFileWithComments();
await viewerPage.goToViewer({
fileId: singleBoardFileId,
pageId: singleBoardPageId,
});
await viewerPage.showComments();
await viewerPage.showCommentsThread(1);
await expect(
viewerPage.page.getByRole("textbox", { name: "Reply" }),
).toBeVisible();
await expect(viewerPage.page).toHaveScreenshot();
});
test("User opens Viewer comment list", async ({ page }) => {
const viewerPage = new ViewerPage(page);
await viewerPage.setupLoggedInUser();
await viewerPage.setupFileWithComments();
await viewerPage.goToViewer({
fileId: singleBoardFileId,
pageId: singleBoardPageId,
});
await viewerPage.showComments();
await viewerPage.page.getByTestId("viewer-comments-dropdown").click();
await viewerPage.page.getByText("Show comments list").click();
await expect(
viewerPage.page.getByRole("button", { name: "Show all comments" }),
).toBeVisible();
await expect(viewerPage.page).toHaveScreenshot();
});
test("User goes to the Viewer Inspect code", async ({ page }) => {
const viewerPage = new ViewerPage(page);
await viewerPage.setupLoggedInUser();
await viewerPage.setupFileWithComments();
await viewerPage.goToViewer({
fileId: singleBoardFileId,
pageId: singleBoardPageId,
});
await viewerPage.showCode();
await expect(viewerPage.page.getByText("Size and position")).toBeVisible();
await expect(viewerPage.page).toHaveScreenshot();
});
test("User goes to the Viewer Inspect code, code tab", async ({ page }) => {
const viewerPage = new ViewerPage(page);
await viewerPage.setupLoggedInUser();
await viewerPage.setupFileWithComments();
await viewerPage.goToViewer({
fileId: singleBoardFileId,
pageId: singleBoardPageId,
});
await viewerPage.showCode();
await viewerPage.page.getByTestId("code").click();
await expect(
viewerPage.page.getByRole("button", { name: "Copy all code" }),
).toBeVisible();
await expect(viewerPage.page).toHaveScreenshot();
});
test("User opens Share modal", async ({ page }) => {
const viewerPage = new ViewerPage(page);
await viewerPage.setupLoggedInUser();
await viewerPage.setupFileWithSingleBoard();
await viewerPage.goToViewer({
fileId: singleBoardFileId,
pageId: singleBoardPageId,
});
await viewerPage.page.getByRole("button", { name: "Share" }).click();
await expect(
viewerPage.page.getByRole("button", { name: "Get link" }),
).toBeVisible();
await expect(viewerPage.page).toHaveScreenshot();
});

View File

@@ -0,0 +1,150 @@
import { test, expect } from "@playwright/test";
import { WorkspacePage } from "../pages/WorkspacePage";
test.beforeEach(async ({ page }) => {
await WorkspacePage.init(page);
});
const setupFileWithAssets = async (workspace) => {
const fileId = "015fda4f-caa6-8103-8004-862a00dd4f31";
const pageId = "015fda4f-caa6-8103-8004-862a00ddbe94";
const fragments = {
"015fda4f-caa6-8103-8004-862a9e4b4d4b":
"assets/get-file-fragment-with-assets-components.json",
"015fda4f-caa6-8103-8004-862a9e4ad279":
"assets/get-file-fragmnet-with-assets-page.json",
};
await workspace.setupEmptyFile();
await workspace.mockRPC(/get\-file\?/, "assets/get-file-with-assets.json");
for (const [id, fixture] of Object.entries(fragments)) {
await workspace.mockRPC(
`get-file-fragment?file-id=*&fragment-id=${id}`,
fixture,
);
}
return { fileId, pageId };
};
test("Shows the workspace correctly for a blank file", async ({ page }) => {
const workspace = new WorkspacePage(page);
await workspace.setupEmptyFile();
await workspace.goToWorkspace();
await expect(workspace.page).toHaveScreenshot();
});
test.describe("Design tab", () => {
test("Shows the design tab when selecting a shape", async ({ page }) => {
const workspace = new WorkspacePage(page);
await workspace.setupEmptyFile();
await workspace.mockRPC(/get\-file\?/, "workspace/get-file-not-empty.json");
await workspace.goToWorkspace({
fileId: "6191cd35-bb1f-81f7-8004-7cc63d087374",
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
});
await workspace.clickLeafLayer("Rectangle");
await expect(workspace.page).toHaveScreenshot();
});
test("Shows expanded sections of the design tab", async ({ page }) => {
const workspace = new WorkspacePage(page);
await workspace.setupEmptyFile();
await workspace.mockRPC(/get\-file\?/, "workspace/get-file-not-empty.json");
await workspace.goToWorkspace({
fileId: "6191cd35-bb1f-81f7-8004-7cc63d087374",
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
});
await workspace.clickLeafLayer("Rectangle");
await workspace.rightSidebar.getByTestId("add-stroke").click();
await expect(workspace.page).toHaveScreenshot();
});
});
test.describe("Assets tab", () => {
test("Shows the libraries modal correctly", async ({ page }) => {
const workspace = new WorkspacePage(page);
await workspace.setupEmptyFile();
await workspace.mockRPC(
"link-file-to-library",
"workspace/link-file-to-library.json",
);
await workspace.mockRPC(
"get-team-shared-files?team-id=*",
"workspace/get-team-shared-libraries-non-empty.json",
);
await workspace.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspace.goToWorkspace();
await workspace.clickAssets();
await workspace.openLibrariesModal();
await expect(workspace.page).toHaveScreenshot();
await workspace.clickLibrary("Testing library 1");
await expect(
workspace.librariesModal.getByText(
"There are no Shared Libraries available",
),
).toBeVisible();
await expect(workspace.page).toHaveScreenshot();
});
test("Shows the assets correctly", async ({ page }) => {
const workspace = new WorkspacePage(page);
const { fileId, pageId } = await setupFileWithAssets(workspace);
await workspace.goToWorkspace({ fileId, pageId });
await workspace.clickAssets();
await workspace.sidebar.getByRole("button", { name: "Components" }).click();
await workspace.sidebar.getByRole("button", { name: "Colors" }).click();
await workspace.sidebar
.getByRole("button", { name: "Typographies" })
.click();
await expect(workspace.page).toHaveScreenshot();
await workspace.sidebar.getByTitle("List view").click();
await expect(workspace.page).toHaveScreenshot();
});
});
test.describe("Palette", () => {
test("Shows the bottom palette expanded and collapsed", async ({ page }) => {
const workspace = new WorkspacePage(page);
const { fileId, pageId } = await setupFileWithAssets(workspace);
await workspace.goToWorkspace({ fileId, pageId });
await expect(workspace.page).toHaveScreenshot();
await workspace.palette
.getByRole("button", { name: "Typographies" })
.click();
await expect(
workspace.palette.getByText("Source Sans Pro Regular"),
).toBeVisible();
await expect(workspace.page).toHaveScreenshot();
await workspace.palette
.getByRole("button", { name: "Color Palette" })
.click();
await expect(
workspace.palette.getByRole("button", { name: "#7798ff" }),
).toBeVisible();
});
});

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 15.674"><path fill="#fff" fill-rule="evenodd" d="M7.976 0C3.566 0 0 3.592 0 8.035a8.03 8.03 0 0 0 5.454 7.623c.396.08.541-.173.541-.385 0-.187-.013-.825-.013-1.49-2.219.479-2.681-.958-2.681-.958-.356-.932-.885-1.171-.885-1.171-.726-.492.053-.492.053-.492.806.053 1.229.825 1.229.825.713 1.223 1.862.878 2.324.665.066-.519.277-.878.502-1.078-1.77-.186-3.632-.878-3.632-3.964 0-.878.317-1.597.819-2.155-.079-.2-.357-1.025.079-2.129 0 0 .674-.213 2.192.825a7.633 7.633 0 0 1 3.988 0c1.519-1.038 2.192-.825 2.192-.825.436 1.104.159 1.929.079 2.129.516.558.819 1.277.819 2.155 0 3.086-1.862 3.765-3.644 3.964.29.253.541.732.541 1.49 0 1.078-.013 1.943-.013 2.208 0 .213.145.466.541.386a8.028 8.028 0 0 0 5.454-7.623C15.952 3.592 12.374 0 7.976 0Z" class="fills" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 852 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="screenshot-43864c00-8517-80fc-8004-14a71e4f14d1" fill="none" version="1.1" viewBox="0 0 16 16"><g id="shape-43864c00-8517-80fc-8004-14a71e4f14d1" fill="#000"><defs id="shape-43864c00-8517-80fc-8004-14a71e4f65d8" fill="#000"><style id="shape-43864c00-8517-80fc-8004-14a71e4f65da">.cls-2{fill:#fc6d26}</style></defs><g id="shape-43864c00-8517-80fc-8004-14a71e4f65d9" fill="#000"><g id="shape-43864c00-8517-80fc-8004-14a71e4f65dc"><path id="fills-43864c00-8517-80fc-8004-14a71e4f65dc" fill="#e24329" d="M15.733 6.099Zl-2.2-5.741a.561.561 0 0 0-.224-.269.583.583 0 0 0-.666.035.587.587 0 0 0-.194.294l-1.47 4.498H5.025L3.555.418a.57.57 0 0 0-.194-.294.581.581 0 0 0-.666-.036.57.57 0 0 0-.224.27L.289 6.038l-.022.058a4.043 4.043 0 0 0 1.342 4.673l.007.006.02.014 3.317 2.485 1.642 1.242.999.754a.67.67 0 0 0 .813 0l1-.754 1.641-1.242 3.337-2.5.009-.006a4.045 4.045 0 0 0 1.339-4.669Z" class="fills"/></g><g id="shape-43864c00-8517-80fc-8004-14a71e4f65dd"><g id="fills-43864c00-8517-80fc-8004-14a71e4f65dd" class="fills"><path d="M15.733 6.099Zc-1.083.16-2.083.61-2.95 1.259L8 10.974l3.047 2.303 3.337-2.499.008-.007a4.045 4.045 0 0 0 1.341-4.672Z" class="cls-2"/></g></g><g id="shape-43864c00-8517-80fc-8004-14a71e4f65de"><path id="fills-43864c00-8517-80fc-8004-14a71e4f65de" fill="#fca326" d="m4.953 13.277 1.642 1.242.999.755a.674.674 0 0 0 .813 0l1-.755 1.641-1.242S9.629 12.203 8 10.974c-1.629 1.229-3.047 2.303-3.047 2.303Z" class="fills"/></g><g id="shape-43864c00-8517-80fc-8004-14a71e4f65df"><g id="fills-43864c00-8517-80fc-8004-14a71e4f65df" class="fills"><path d="M3.217 7.358a7.364 7.364 0 0 0-2.928-1.32l-.022.058a4.043 4.043 0 0 0 1.342 4.673l.007.006.02.014 3.317 2.485L8 10.971Z" class="cls-2"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/><path fill="none" d="M1 1h22v22H1z"/></svg>

After

Width:  |  Height:  |  Size: 719 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><g fill="#000"><path fill="#b2b2b2" d="M7.339 13.094v.207c-2.477-.311-4.354-1.669-4.354-3.305 0-1.229 1.058-2.3 2.64-2.891l-.062-1.48C2.778 6.328.813 8.019.813 9.996c0 2.384 2.839 4.348 6.529 4.678h.001l-.004-1.58Z" class="fills"/><path fill="#f7931e" d="M9.524 13.647ZL9.521 1.326 7.339 2.445v4.482h.004v7.747Z" class="fills"/><path fill="#b2b2b2" d="m15.187 9.123-.295-3.128-1.123.635c-.798-.485-1.816-1.083-3.144-1.091v1.372c.11.031.216.064.322.098.442.144.849.324 1.208.535l-1.181.664 4.213.915Z" class="fills"/></g></svg>

After

Width:  |  Height:  |  Size: 598 B

View File

@@ -0,0 +1 @@
<svg height="182" viewBox="0 0 677.34762 182.15429" width="667" xmlns="http://www.w3.org/2000/svg"><path d="m128.273 0-3.9 2.77-124.373 88.308 128.273 91.076 549.075-.006v-182.14zm20.852 30 498.223.006v122.144l-498.223.007zm-25 9.74v102.678l-49.033-34.813-.578-32.64 49.61-35.225z"/><path d="m134.482 157.147v25l518.57.008.002-25z"/></svg>

After

Width:  |  Height:  |  Size: 339 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 279 KiB

View File

@@ -0,0 +1,3 @@
<svg width="79" xmlns="http://www.w3.org/2000/svg" height="1537" viewBox="2462.648 7069.559 79 1537">
<path d="m2483.255 7069.559-10.973 15.432v1441.521l-9.547 4.535-.087-.039v56.915l37.226 17.564 2.274 1.072 2.276-1.072 37.224-17.564v-56.915l-.07.032-9.55-4.536V7084.991l-.334-.468-10.639-14.964-10.973 15.432v.016l-7.983-11.231-7.923 11.144-.282-.397-10.639-14.964Zm1.941 8.964 4.244 5.968h-12.37l4.195-5.899 3.931-.069Zm37.8 0 4.244 5.968h-12.37l4.193-5.899 3.933-.069Zm-18.956 4.219 4.244 5.968h-12.37l4.194-5.898 3.932-.07Zm-28.143 4.758h5.866v1452.525l-5.866-2.768V7087.5Zm8.878 0h5.838v1456.7l-5.838-2.754V7087.5Zm28.923 0h5.866v1453.925l-5.866 2.768V7087.5Zm8.877 0h5.838v1449.75l-5.838 2.754V7087.5Zm-27.834 4.219h5.866v1457.196l-5.866-2.768V7091.719Zm8.879 0h5.837v1454.476l-5.837 2.754v-1457.23Zm28.407 1439.28 5.165 2.11-5.165 2.437v-4.547Zm-59.745.009v4.544l-5.164-2.436 5.164-2.108Zm-5.084 7.17 32.676 15.416v46.867l-32.676-15.415v-46.868Zm69.901 0v46.868l-32.675 15.415v-46.867l32.675-15.416Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1020 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="m2.5 4 .707 1.225M2.5 4l1.225-.707M2.5 4C5.542 4.292 8 7.5 8 11.5c0-4 2.411-7.202 5.5-7.5M8 15.5c0-6-2.5-7-7-7m7 7c0-6 2.5-7 7-7m-7 7V1M1 8.5l1 1m-1-1 1-1M8 1 7 2m1-1 1 1m4.5 2-.707 1.224M13.5 4l-1.225-.707M15 8.5l-1 1m1-1-1-1"/>
</svg>

After

Width:  |  Height:  |  Size: 358 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M14.5 7c0-2.5-.5-4-4.501-4m0 0L11.5 4.5M9.999 3 11.5 1.5M1.5 9c0 2.5.5 4 4.501 4m0 0L4.5 11.5M6.001 13 4.5 14.5m5.5.5h4a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1M6 1H2a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1"/>
</svg>

After

Width:  |  Height:  |  Size: 383 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M7 3.5H3.125a1 1 0 0 0-.43.099q-.207.099-.366.281-.157.183-.244.422A1.5 1.5 0 0 0 2 4.8v9.4c0 .345.118.676.33.92.21.243.496.38.795.38h6.75c.298 0 .584-.137.795-.38a1.4 1.4 0 0 0 .33-.92V8M7 3.5 11 8M7 3.5V8h4m2-7c-.5 1.5-1 2-2 2.5 1 .5 1.5 1 2 2.5.5-1.5 1-2 2-2.5-1-.5-1.5-1-2-2.5Z"/>
</svg>

After

Width:  |  Height:  |  Size: 413 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M12.883 7.718a.399.399 0 0 1 0 .564l-4.601 4.601a.399.399 0 0 1-.564 0L3.117 8.282a.399.399 0 0 1 0-.564m9.766 2.5a.399.399 0 0 1 0 .564l-4.601 4.601a.399.399 0 0 1-.564 0l-4.601-4.601a.399.399 0 0 1 0-.564M8.282.617l4.601 4.601a.399.399 0 0 1 0 .564l-4.601 4.601a.399.399 0 0 1-.564 0L3.117 5.782a.399.399 0 0 1 0-.564L7.718.617a.399.399 0 0 1 .564 0Zm-.197 3.418 1.38 1.38a.12.12 0 0 1 0 .17l-1.38 1.38a.12.12 0 0 1-.17 0l-1.38-1.38a.12.12 0 0 1 0-.17l1.38-1.38a.12.12 0 0 1 .17 0Z"/>
</svg>

After

Width:  |  Height:  |  Size: 613 B

View File

@@ -0,0 +1 @@
<svg height="500" viewBox="0 0 500 500.00001" width="500" xmlns="http://www.w3.org/2000/svg"><path d="m159.4607 552.36219-52.57675 74.05348v41.86098l-45.753774 21.76184-.412151-.19478v17.20283 255.89707l178.379885 84.27239 10.90209 5.1462 10.89926-5.1462 178.38271-84.27239v-273.0999l-.33593.15808-45.76789-21.76749v-41.81863l-1.60059-2.25268-50.97899-71.8008-52.57958 74.05348v.0734l-38.25894-53.88377-37.96254 53.4688-1.35782-1.91111zm9.3015 43.01555 20.33627 28.64128h-59.27553l20.09914-28.30535zm181.13787 0 20.33626 28.64128h-59.27553l20.09632-28.30535zm-90.83852 20.24593 20.33626 28.63846h-59.2727l20.09631-28.30535zm-134.85903 22.82891h28.11339v94.66356l-28.11339-13.2818zm42.54695 0h27.97224l-.003 114.69495-27.97224-13.21405zm138.58809 0h28.11622l-.003 101.38492-28.11057 13.27898-.003-114.6639zm42.54695 0h27.97224v81.3507l-27.97224 13.21406zm-133.38265 20.24311h28.11339v117.07749l-28.11339-13.2818zm42.54695 0h27.97225l-.003 104.02152-27.97224 13.21688.003-117.2384zm136.12651 31.11133 24.75131 10.12014-24.75131 11.6925zm-286.29137.0367v21.80982l-24.748483-11.6925zm-24.367392 34.4113 156.581352 73.96879v224.87888l-156.581352-73.96877zm334.964042 0v224.8789l-156.58134 73.96877v-224.87888z" transform="translate(0 -552.3622)"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 13L5.33348 9.60702M11.8597 13L10.5264 9.60702M10.5264 9.60702L7.93011 3L5.33348 9.60702M10.5264 9.60702H5.33348"/>
</svg>

After

Width:  |  Height:  |  Size: 246 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M5 7.99869H8.62494M5 7.99869V12.9974L9.14433 13C10.5247 13 11.6437 11.881 11.6437 10.5007C11.6437 9.57453 11.1399 8.76604 10.3915 8.33425C10.1503 8.1225 9.07914 7.99869 8.62494 7.99869M5 7.99869L5.00037 3H8.62531C9.07951 3 9.50541 3.12116 9.87246 3.33291C10.6209 3.7647 11.1246 4.57319 11.1246 5.49933C11.1246 6.87967 10.0053 7.99869 8.62494 7.99869"/>
</svg>

After

Width:  |  Height:  |  Size: 481 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 4.85704C11.175 3.72426 9.70088 3 8.28725 3C5.80196 3 4 5.23858 4 8C4 10.7614 5.80196 13 8.28725 13C9.70088 13 11.175 12.2757 12 11.143"/>
</svg>

After

Width:  |  Height:  |  Size: 270 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M5 3H6.7002C8.7002 3 11.7002 4.63551 11.7002 7.8257C11.7002 11.6595 8.7002 13 6.7002 13H5V3Z"/>
</svg>

After

Width:  |  Height:  |  Size: 224 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M5 8.00134L9.50061 7.99869M5 8.00134V13H10.5721M5 8.00134L5.00037 3.00265L10.5721 3"/>
</svg>

After

Width:  |  Height:  |  Size: 215 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M5 13V8.00134M5 8.00134L9.50061 7.99869M5 8.00134L5.00037 3.00265L10.5721 3"/>
</svg>

After

Width:  |  Height:  |  Size: 207 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 4.85704C11.175 3.72426 9.70088 3 8.28725 3C5.80196 3 4 5.23858 4 8C4 10.7614 5.80196 13 8.28725 13C11.2515 13 12.5745 10.5027 12 8.01573L8.5 8"/>
</svg>

After

Width:  |  Height:  |  Size: 278 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M4.5 13V8.00002M4.5 8.00002L11.1454 7.99737M4.5 8.00002L4.50037 3M11.1454 7.99737V3M11.1454 7.99737V13"/>
</svg>

After

Width:  |  Height:  |  Size: 234 B

Some files were not shown because too many files have changed in this diff Show More