chore: merge qa automation repo as subfolder
Because as far as we know there's no good reason why Volodimir insisted on it being a separate repository. The merging of `desktop-qa-automation` was one using [`git-filter-repo`](https://github.com/newren/git-filter-repo) tool and these instructions: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731 Steps: ```sh git clone git@github.com:status-im/status-desktop git clone git@github.com:status-im/desktop-qa-automation cd desktop-qa-automation git filter-repo --to-subdirectory-filter test/e2e cd ../status-desktop git remote add test-e2e ../desktop-qa-automation git fetch test-e2e --no-tags git merge --no-edit --allow-unrelated-histories test-e2e/master ``` Here's proof that it can work for a nightly, but will definitely need more adjustments: https://ci.status.im/job/status-desktop/job/systems/job/linux/job/x86_64/job/tests-e2e/3/ And here's and example PR E2E run: https://ci.status.im/job/status-desktop/job/e2e/job/prs-merged/2/ Signed-off-by: Jakub Sokołowski <jakub@status.im>
|
@ -0,0 +1,16 @@
|
|||
|
||||
configs/_local.py
|
||||
|
||||
.idea/
|
||||
.venv/
|
||||
|
||||
*.pyc
|
||||
|
||||
tmp/
|
||||
|
||||
*.DS_Store
|
||||
|
||||
/local_run_results/
|
||||
squish.ini
|
||||
.envrc
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# This repository manages UI tests for desktop application
|
||||
|
||||
## How to set up your environment
|
||||
|
||||
1. **MacOS**: https://www.notion.so/Mac-arch-x64-and-Intel-50ea48dae1d4481b882afdbfad38e95a
|
||||
2. **Linux**: https://www.notion.so/Linux-21f7abd2bb684a0fb10057848760a889
|
||||
3. **Windows**: https://www.notion.so/Windows-fbccd2b09b784b32ba4174233d83878d
|
||||
|
||||
**NOTE:** when MacOS and Linux are proven to be working, Windows guide could be outdated (no one yet set up Windows)
|
||||
|
||||
## Which build to use
|
||||
|
||||
1. you _can_ use your local dev build but sometimes tests hag there. To use it, just place a path to the executable to AUT_PATH in your _local.py config,
|
||||
for example `AUT_PATH = "/Users/anastasiya/status-desktop/bin/nim_status_client"`
|
||||
|
||||
2. normally, please use CI build. Grab recent one from Jenkins job https://ci.status.im/job/status-desktop/job/nightly/
|
||||
|
||||
**2.1** Linux and Windows could be taken from nightly job
|
||||
![img.png](img.png)
|
||||
|
||||
**2.2** Mac **requires entitlements** for Squish which we don't add by default, so please go here https://ci.status.im/job/status-desktop/job/systems/job/macos/
|
||||
and select architecture you need (arm or intel), click Build with parameters and select Squish entitlements. Select a branch if u like (master is default)
|
||||
![img_1.png](img_1.png)
|
||||
|
||||
## Pytest marks used
|
||||
|
||||
You can run tests by mark, just use it like this in command line:
|
||||
|
||||
```bash
|
||||
python3 -m pytest -m critical
|
||||
```
|
||||
|
||||
or directly in pycharm terminal:
|
||||
|
||||
```bash
|
||||
pytest -m critical
|
||||
```
|
||||
|
||||
You can obtain the list of all marks we have by running this `pytest --markers`
|
||||
|
||||
- `critical`, mark used to select the most important checks we do for PRs in desktop repository
|
||||
(the same for our repo PRs)
|
||||
- `xfail`, used to link tests to existing tickets in desktop, so if test fails it will be marked as
|
||||
expected to fail in report with a reference to the ticket. At the same time, if such test is passing,
|
||||
it will be shown as XPASS (unexpectedly passing) in report, which will indicate the initial bug is gone
|
||||
- `skip`, used to just skip tests for various reasons, normally with a ticket linked
|
||||
- `flaky`, used to mark the tests that are normally passing but sometimes fail. If such test passes, then
|
||||
if will be shown as passed in report normally. If the test fails, then the total run wont be failed, but
|
||||
the corresponding test will be marked as `xfail` in the report. It is done for a few tests that are not super
|
||||
stable yet, but passes most of the time. This mark should be used with caution and in case of real need only.
|
||||
- `timeout(timeout=180, method="thread")`, to catch excessively long test durations like deadlocked or hanging tests.
|
||||
This is done by `pytest-timeout` plugin
|
|
@ -0,0 +1,251 @@
|
|||
#!/usr/bin/env groovy
|
||||
library 'status-jenkins-lib@v1.8.6'
|
||||
|
||||
pipeline {
|
||||
|
||||
agent {
|
||||
label "${params.AGENT} && x86_64 && qt-5.15.2"
|
||||
}
|
||||
|
||||
parameters {
|
||||
gitParameter(
|
||||
name: 'GIT_REF',
|
||||
description: 'Git branch to checkout.',
|
||||
branchFilter: 'origin/(.*)',
|
||||
branch: '',
|
||||
defaultValue: 'master',
|
||||
quickFilterEnabled: false,
|
||||
selectedValue: 'DEFAULT',
|
||||
sortMode: 'ASCENDING_SMART',
|
||||
tagFilter: '*',
|
||||
type: 'PT_BRANCH'
|
||||
)
|
||||
string(
|
||||
name: 'BUILD_SOURCE',
|
||||
description: 'URL to tar.gz file OR path to Jenkins build.',
|
||||
defaultValue: getDefaultBuildSource()
|
||||
)
|
||||
string(
|
||||
name: 'TEST_NAME',
|
||||
description: 'Paste test name/part of test name to run specific test.',
|
||||
defaultValue: ''
|
||||
)
|
||||
string(
|
||||
name: 'TEST_SCOPE_FLAG',
|
||||
description: 'Paste a known mark to run tests labeled with this mark',
|
||||
defaultValue: getDefaultTestScopeFlag()
|
||||
)
|
||||
string(
|
||||
name: 'TESTRAIL_RUN_NAME',
|
||||
description: 'Test run name in Test Rail.',
|
||||
defaultValue: ''
|
||||
)
|
||||
choice(
|
||||
name: 'LOG_LEVEL',
|
||||
description: 'Log level for pytest.',
|
||||
choices: ['INFO', 'DEBUG', 'TRACE', 'WARNING', 'CRITICAL']
|
||||
)
|
||||
/* FIXME: This is temporary and should be removed. */
|
||||
choice(
|
||||
name: 'AGENT',
|
||||
description: 'Agent name to run tests on it.',
|
||||
choices: ['linux', 'linux-01', 'linux-02', 'linux-03', 'linux-04', 'linux-05']
|
||||
)
|
||||
}
|
||||
|
||||
options {
|
||||
timestamps()
|
||||
/* Prevent Jenkins jobs from running forever */
|
||||
timeout(time: 120, unit: 'MINUTES')
|
||||
/* manage how many builds we keep */
|
||||
buildDiscarder(logRotator(
|
||||
daysToKeepStr: '60',
|
||||
numToKeepStr: '50',
|
||||
artifactNumToKeepStr: '50',
|
||||
))
|
||||
}
|
||||
|
||||
environment {
|
||||
SQUISH_DIR = '/opt/squish-runner-7.2.1'
|
||||
PYTHONPATH = "${SQUISH_DIR}/lib:${SQUISH_DIR}/lib/python:${PYTHONPATH}"
|
||||
LD_LIBRARY_PATH = "${SQUISH_DIR}/lib:${SQUISH_DIR}/python3/lib:${LD_LIBRARY_PATH}"
|
||||
|
||||
/* Avoid race conditions with other builds using virtualenv. */
|
||||
VIRTUAL_ENV = "${WORKSPACE_TMP}/venv"
|
||||
PATH = "${VIRTUAL_ENV}/bin:${PATH}"
|
||||
|
||||
TESTRAIL_URL = 'https://ethstatus.testrail.net'
|
||||
TESTRAIL_PROJECT_ID = 17
|
||||
|
||||
/* Runtime flag to make testing of the app easier. Switched off: unpredictable app behavior under new tests */
|
||||
/* STATUS_RUNTIME_TEST_MODE = 'True' */
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Prep') {
|
||||
steps { script {
|
||||
setNewBuildName()
|
||||
updateGitHubStatus()
|
||||
} }
|
||||
}
|
||||
|
||||
stage('Deps') {
|
||||
steps { script {
|
||||
sh "python3 -m venv ${VIRTUAL_ENV}"
|
||||
sh 'pip3 install -r requirements.txt'
|
||||
} }
|
||||
}
|
||||
|
||||
stage('Download') {
|
||||
when { expression { params.BUILD_SOURCE.startsWith('http') } }
|
||||
steps { timeout(5) { script {
|
||||
sh 'mkdir -p ./pkg/'
|
||||
setBuildDescFromFile(params.BUILD_SOURCE)
|
||||
fileOperations([
|
||||
fileDownloadOperation(
|
||||
url: params.BUILD_SOURCE,
|
||||
targetFileName: 'StatusIm-Desktop.tar.gz',
|
||||
targetLocation: './pkg/',
|
||||
userName: '',
|
||||
password: '',
|
||||
)
|
||||
])
|
||||
} } }
|
||||
}
|
||||
|
||||
stage('Copy') {
|
||||
when { expression { ! params.BUILD_SOURCE.startsWith('http') } }
|
||||
steps { timeout(5) { script {
|
||||
copyArtifacts(
|
||||
projectName: params.BUILD_SOURCE,
|
||||
filter: 'pkg/*-x86_64.tar.gz',
|
||||
selector: lastWithArtifacts(),
|
||||
target: './'
|
||||
)
|
||||
setBuildDescFromFile(utils.findFile('pkg/*tar.gz'))
|
||||
} } }
|
||||
}
|
||||
|
||||
stage('Unpack') {
|
||||
steps { timeout(5) { script {
|
||||
sh 'mkdir aut'
|
||||
sh "tar -zxvf '${utils.findFile('pkg/*tar.gz')}' -C './aut'"
|
||||
env.AUT_PATH = utils.findFile('aut/*.AppImage')
|
||||
} } }
|
||||
}
|
||||
|
||||
stage('Test') {
|
||||
steps { timeout(90) { script {
|
||||
def flags = []
|
||||
if (params.TEST_NAME) { flags.add("-k=${params.TEST_NAME}") }
|
||||
if (params.TEST_SCOPE_FLAG) { flags.add(params.TEST_SCOPE_FLAG) }
|
||||
if (params.LOG_LEVEL) { flags.addAll(["--log-level=${params.LOG_LEVEL}", "--log-cli-level=${params.LOG_LEVEL}"]) }
|
||||
dir ('configs') { sh 'ln -s _local.ci.py _local.py' }
|
||||
wrap([
|
||||
$class: 'Xvfb',
|
||||
autoDisplayName: true,
|
||||
parallelBuild: true,
|
||||
screen: '1920x1080x24',
|
||||
additionalOptions: '-dpi 1'
|
||||
]) {
|
||||
sh 'fluxbox &'
|
||||
withCredentials([
|
||||
usernamePassword(
|
||||
credentialsId: 'test-rail-api-devops',
|
||||
usernameVariable: 'TESTRAIL_USR',
|
||||
passwordVariable: 'TESTRAIL_PSW'
|
||||
)
|
||||
]) {
|
||||
/* Keep the --reruns flag first, or it won't work */
|
||||
sh """
|
||||
python3 -m pytest --reruns=1 ${flags.join(" ")} \
|
||||
--disable-warnings \
|
||||
--alluredir=./allure-results
|
||||
"""
|
||||
}
|
||||
}
|
||||
} } }
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always { script {
|
||||
archiveArtifacts('aut/*.log')
|
||||
|
||||
/* Needed to categorize types of errors and add environment section in allure report. */
|
||||
sh 'cp ext/allure_files/categories.json allure-results'
|
||||
sh 'cp ext/allure_files/environment.properties allure-results'
|
||||
|
||||
allure([
|
||||
results: [[path: 'allure-results']],
|
||||
reportBuildPolicy: 'ALWAYS',
|
||||
properties: [],
|
||||
jdk: '',
|
||||
])
|
||||
updateGitHubStatus()
|
||||
} }
|
||||
failure { script {
|
||||
discord.send(
|
||||
header: '**Desktop E2E test failure!**',
|
||||
cred: 'discord-status-desktop-e2e-webhook',
|
||||
)
|
||||
} }
|
||||
cleanup { cleanWs() }
|
||||
}
|
||||
}
|
||||
|
||||
def setNewBuildName() {
|
||||
if (currentBuild.upstreamBuilds) {
|
||||
def parent = utils.parentOrCurrentBuild()
|
||||
currentBuild.displayName = parent.getFullDisplayName().minus('status-desktop » ')
|
||||
}
|
||||
}
|
||||
|
||||
def setBuildDescFromFile(fileNameOrPath) {
|
||||
def tokens = utils.parseFilename(utils.baseName(fileNameOrPath))
|
||||
if (tokens == null) { /* Fallback for regex fail. */
|
||||
currentBuild.description = utils.baseName(fileNameOrPath)
|
||||
return
|
||||
}
|
||||
if (tokens.build && tokens.build.startsWith('pr')) {
|
||||
currentBuild.displayName = tokens.build.replace(/^pr/, 'PR-')
|
||||
}
|
||||
currentBuild.description = formatMap([
|
||||
Node: NODE_NAME,
|
||||
Build: tokens.build,
|
||||
Commit: tokens.commit,
|
||||
Version: (tokens.tstamp ?: tokens.version),
|
||||
])
|
||||
}
|
||||
|
||||
def updateGitHubStatus() {
|
||||
/* For PR builds update check status. */
|
||||
if (params.BUILD_SOURCE ==~ /.*\/PR-[0-9]+\/?$/) {
|
||||
github.statusUpdate(
|
||||
context: 'jenkins/prs/tests/e2e-new',
|
||||
commit: jenkins.getJobCommitByPath(params.BUILD_SOURCE),
|
||||
repo_url: 'https://github.com/status-im/status-desktop'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def formatMap(Map data=[:]) {
|
||||
def text = ''
|
||||
data.each { key, val -> text += "<b>${key}</b>: ${val}</a><br>\n" }
|
||||
return text
|
||||
}
|
||||
|
||||
def getDefaultBuildSource() {
|
||||
if (JOB_NAME ==~ 'status-desktop/qa-automation/prs/.*') {
|
||||
return 'status-desktop/nightly'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
def getDefaultTestScopeFlag() {
|
||||
if (JOB_NAME == "status-desktop/systems/linux/x86_64/tests-e2e-new") {
|
||||
return ''
|
||||
} else {
|
||||
return '-m=critical'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import logging
|
||||
|
||||
from os import path
|
||||
from scripts.utils.system_path import SystemPath
|
||||
from . import testpath, timeouts, testrail, squish, system
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from ._local import *
|
||||
except ImportError:
|
||||
exit(
|
||||
'Config file: "_local.py" not found in "./configs".\n'
|
||||
'Please use template "_.local.default.py" to create file or execute command: \n'
|
||||
rf'cp {testpath.ROOT}/configs/_local.default.py {testpath.ROOT}/configs/_local.py'
|
||||
)
|
||||
|
||||
if AUT_PATH is None:
|
||||
exit('Please add "AUT_PATH" in ./configs/_local.py')
|
||||
if system.IS_WIN and 'bin' not in AUT_PATH:
|
||||
exit('Please use launcher from "bin" folder in "AUT_PATH"')
|
||||
AUT_PATH = SystemPath(AUT_PATH)
|
||||
|
||||
# Save application logs
|
||||
AUT_DIR = path.dirname(AUT_PATH)
|
||||
PYTEST_LOG = path.join(AUT_DIR, 'pytest.log')
|
||||
AUT_LOG_FILE = path.join(AUT_DIR, 'aut.log')
|
||||
SQUISH_LOG_FILE = path.join(AUT_DIR, 'squish.log')
|
|
@ -0,0 +1,7 @@
|
|||
import os
|
||||
import logging
|
||||
|
||||
LOG_LEVEL = logging.getLevelName(os.getenv('LOG_LEVEL', 'INFO'))
|
||||
UPDATE_VP_ON_FAIL = False
|
||||
DEV_BUILD = False
|
||||
AUT_PATH = os.getenv('AUT_PATH')
|
|
@ -0,0 +1,6 @@
|
|||
import logging
|
||||
|
||||
LOG_LEVEL = logging.DEBUG
|
||||
UPDATE_VP_ON_FAIL = False
|
||||
DEV_BUILD = False
|
||||
AUT_PATH = None
|
|
@ -0,0 +1,5 @@
|
|||
import os
|
||||
|
||||
AUT_PORT = 61500 + int(os.getenv('BUILD_NUMBER', 0))
|
||||
SERVER_PORT = 4322 + int(os.getenv('BUILD_NUMBER', 0))
|
||||
CURSOR_ANIMATION = False
|
|
@ -0,0 +1,12 @@
|
|||
import os
|
||||
import platform
|
||||
|
||||
IS_LIN = True if platform.system() == 'Linux' else False
|
||||
IS_MAC = True if platform.system() == 'Darwin' else False
|
||||
IS_WIN = True if platform.system() == 'Windows' else False
|
||||
|
||||
OS_ID = 'lin' if IS_LIN else 'mac' if IS_MAC else 'win'
|
||||
|
||||
DISPLAY = os.getenv('DISPLAY', ':0')
|
||||
|
||||
TEST_MODE = os.getenv('STATUS_RUNTIME_TEST_MODE')
|
|
@ -0,0 +1,33 @@
|
|||
import os
|
||||
import typing
|
||||
from datetime import datetime
|
||||
|
||||
from scripts.utils.system_path import SystemPath
|
||||
|
||||
ROOT: SystemPath = SystemPath(__file__).resolve().parent.parent
|
||||
|
||||
# Runtime initialisation
|
||||
TEST: typing.Optional[SystemPath] = None
|
||||
TEST_VP: typing.Optional[SystemPath] = None
|
||||
TEST_ARTIFACTS: typing.Optional[SystemPath] = None
|
||||
|
||||
# Test Directories
|
||||
RUN_ID = os.getenv('RUN_DIR', f'run_{datetime.today().strftime("%Y-%m-%d %H:%M:%S")}')
|
||||
RESULTS: SystemPath = ROOT / 'local_run_results'
|
||||
RUN: SystemPath = RESULTS / RUN_ID
|
||||
VP: SystemPath = ROOT / 'ext' / 'vp'
|
||||
TEST_FILES: SystemPath = ROOT / 'ext' / 'test_files'
|
||||
TEST_IMAGES: SystemPath = ROOT / 'ext' / 'test_images'
|
||||
TEST_USER_DATA: SystemPath = ROOT / 'ext' / 'user_data'
|
||||
|
||||
# Driver Directories
|
||||
SQUISH_DIR_RAW = os.getenv('SQUISH_DIR')
|
||||
assert SQUISH_DIR_RAW is not None
|
||||
SQUISH_DIR = SystemPath(SQUISH_DIR_RAW)
|
||||
|
||||
# Status Application
|
||||
STATUS_DATA: SystemPath = RUN
|
||||
|
||||
# Sets log level, can be one of: "ERROR", "WARN", "INFO", "DEBUG", "TRACE". "INFO"
|
||||
LOG_LEVEL = 'DEBUG'
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import os
|
||||
|
||||
CI_BUILD_URL = os.getenv('BUILD_URL', '')
|
||||
|
||||
RUN_NAME = os.getenv('TESTRAIL_RUN_NAME', '')
|
||||
PROJECT_ID = os.getenv('TESTRAIL_PROJECT_ID', '')
|
||||
URL = os.getenv('TESTRAIL_URL', '')
|
||||
USR = os.getenv('TESTRAIL_USR', '')
|
||||
PSW = os.getenv('TESTRAIL_PSW', '')
|
|
@ -0,0 +1,7 @@
|
|||
# Timoeuts before raising errors
|
||||
|
||||
UI_LOAD_TIMEOUT_SEC = 5
|
||||
UI_LOAD_TIMEOUT_MSEC = UI_LOAD_TIMEOUT_SEC * 1000
|
||||
PROCESS_TIMEOUT_SEC = 10
|
||||
APP_LOAD_TIMEOUT_MSEC = 60000
|
||||
MESSAGING_TIMEOUT_SEC = 60
|
|
@ -0,0 +1,70 @@
|
|||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
import os
|
||||
import allure
|
||||
import pytest
|
||||
from PIL import ImageGrab
|
||||
|
||||
import configs
|
||||
from configs.system import IS_LIN
|
||||
from fixtures.path import generate_test_info
|
||||
from scripts.utils.system_path import SystemPath
|
||||
|
||||
# Send logs to pytest.log as well
|
||||
handler = logging.FileHandler(filename=configs.PYTEST_LOG)
|
||||
logging.basicConfig(
|
||||
level=os.getenv('LOG_LEVEL', 'INFO'),
|
||||
format='[%(asctime)s] (%(filename)18s:%(lineno)-3s) [%(levelname)-7s] --- %(message)s',
|
||||
handlers=[handler],
|
||||
)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
pytest_plugins = [
|
||||
'fixtures.aut',
|
||||
'fixtures.path',
|
||||
'fixtures.squish',
|
||||
'fixtures.testrail',
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def setup_session_scope(
|
||||
init_testrail_api,
|
||||
prepare_test_directory,
|
||||
start_squish_server,
|
||||
):
|
||||
LOG.info('Session startup...')
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_function_scope(
|
||||
caplog,
|
||||
generate_test_data,
|
||||
check_result,
|
||||
application_logs
|
||||
):
|
||||
# FIXME: broken due to KeyError: <_pytest.stash.StashKey object at 0x7fd1ba6d78c0>
|
||||
# caplog.set_level(configs.LOG_LEVEL)
|
||||
yield
|
||||
|
||||
|
||||
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
||||
def pytest_runtest_makereport(item, call):
|
||||
outcome = yield
|
||||
rep = outcome.get_result()
|
||||
setattr(item, 'rep_' + rep.when, rep)
|
||||
|
||||
|
||||
def pytest_exception_interact(node):
|
||||
test_path, test_name, test_params = generate_test_info(node)
|
||||
node_dir: SystemPath = configs.testpath.RUN / test_path / test_name / test_params
|
||||
node_dir.mkdir(parents=True, exist_ok=True)
|
||||
screenshot = node_dir / f'screenshot_{datetime.today().strftime("%Y-%m-%d %H:%M:%S")}.png'
|
||||
ImageGrab.grab(xdisplay=configs.system.DISPLAY if IS_LIN else None).save(screenshot)
|
||||
allure.attach(
|
||||
name='Screenshot on fail',
|
||||
body=screenshot.read_bytes(),
|
||||
attachment_type=allure.attachment_type.PNG
|
||||
)
|
|
@ -0,0 +1,5 @@
|
|||
from . import commands
|
||||
from .colors import *
|
||||
from .social_links import *
|
||||
from .tesseract import *
|
||||
from .user import *
|
|
@ -0,0 +1 @@
|
|||
MOCK_KEYCARD = '--test-mode=1'
|
|
@ -0,0 +1,61 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class Color(Enum):
|
||||
WHITE = 1
|
||||
BLACK = 2
|
||||
RED = 3
|
||||
BLUE = 4
|
||||
GREEN = 5
|
||||
YELLOW = 6
|
||||
ORANGE = 7
|
||||
|
||||
|
||||
boundaries = {
|
||||
Color.WHITE: [
|
||||
[0, 0, 0],
|
||||
[0, 0, 255]
|
||||
],
|
||||
Color.BLACK: [
|
||||
[0, 0, 0],
|
||||
[179, 100, 130]
|
||||
],
|
||||
Color.RED: [
|
||||
[
|
||||
[0, 100, 20],
|
||||
[10, 255, 255]
|
||||
],
|
||||
[
|
||||
[160, 100, 20],
|
||||
[179, 255, 255]
|
||||
]
|
||||
],
|
||||
Color.BLUE: [
|
||||
[110, 50, 50],
|
||||
[130, 255, 255]
|
||||
],
|
||||
Color.GREEN: [
|
||||
[36, 25, 25],
|
||||
[70, 255, 255]
|
||||
],
|
||||
Color.YELLOW: [
|
||||
[20, 100, 0],
|
||||
[45, 255, 255]
|
||||
],
|
||||
Color.ORANGE: [
|
||||
[10, 100, 20],
|
||||
[25, 255, 255]
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
class ColorCodes(Enum):
|
||||
GREEN = '#4ebc60'
|
||||
BLUE = '#2a4af5'
|
||||
ORANGE = '#ff9f0f'
|
||||
GRAY = '#939ba1'
|
||||
|
||||
|
||||
class EmojiCodes(Enum):
|
||||
SMILING_FACE_WITH_SUNGLASSES = '1f60e'
|
||||
THUMBSUP_SIGN = '1f44d'
|
|
@ -0,0 +1,13 @@
|
|||
import configs.system
|
||||
|
||||
# Buttons
|
||||
BACKSPACE = 'Backspace'
|
||||
COMMAND = 'Command'
|
||||
CTRL = 'Ctrl'
|
||||
ESCAPE = 'Escape'
|
||||
RETURN = 'Return'
|
||||
SHIFT = 'Shift'
|
||||
|
||||
# Combinations
|
||||
SELECT_ALL = f'{CTRL if configs.system.IS_WIN else COMMAND}+A'
|
||||
OPEN_GOTO = f'{COMMAND}+{SHIFT}+G'
|
|
@ -0,0 +1,60 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class PermissionsElements(Enum):
|
||||
WELCOME_TITLE = "Permissions"
|
||||
WELCOME_SUBTITLE = 'You can manage your community by creating and issuing membership and access permissions'
|
||||
WELCOME_CHECKLIST_ELEMENT_1 = 'Give individual members access to private channels'
|
||||
WELCOME_CHECKLIST_ELEMENT_2 = 'Monetise your community with subscriptions and fees'
|
||||
WELCOME_CHECKLIST_ELEMENT_3 = 'Require holding a token or NFT to obtain exclusive membership rights'
|
||||
|
||||
|
||||
class TokensElements(Enum):
|
||||
WELCOME_TITLE = "Community tokens"
|
||||
WELCOME_SUBTITLE = 'You can mint custom tokens and import tokens for your community'
|
||||
WELCOME_CHECKLIST_ELEMENT_1 = 'Create remotely destructible soulbound tokens for admin permissions'
|
||||
WELCOME_CHECKLIST_ELEMENT_2 = 'Reward individual members with custom tokens for their contribution'
|
||||
WELCOME_CHECKLIST_ELEMENT_3 = 'Mint tokens for use with community and channel permissions'
|
||||
INFOBOX_TITLE = 'Get started'
|
||||
INFOBOX_TEXT = 'In order to Mint, Import and Airdrop community tokens, you first need to mint your Owner token which will give you permissions to access the token management features for your community.'
|
||||
|
||||
|
||||
class MintOwnerTokensElements(Enum):
|
||||
OWNER_TOKEN_CHEKLIST_ELEMENT_1 = 'Only 1 will ever exist'
|
||||
OWNER_TOKEN_CHEKLIST_ELEMENT_2 = 'Hodler is the owner of the Community'
|
||||
OWNER_TOKEN_CHEKLIST_ELEMENT_3 = 'Ability to airdrop / destroy TokenMaster token'
|
||||
OWNER_TOKEN_CHEKLIST_ELEMENT_4 = 'Ability to mint and airdrop Community tokens'
|
||||
MASTER_TOKEN_CHEKLIST_ELEMENT_1 = 'Unlimited supply'
|
||||
MASTER_TOKEN_CHEKLIST_ELEMENT_2 = 'Grants full Community admin rights'
|
||||
MASTER_TOKEN_CHEKLIST_ELEMENT_3 = 'Ability to mint and airdrop Community tokens'
|
||||
MASTER_TOKEN_CHEKLIST_ELEMENT_4 = 'Non-transferrable'
|
||||
MASTER_TOKEN_CHEKLIST_ELEMENT_5 = 'Remotely destructible by the Owner token hodler'
|
||||
SIGN_TRANSACTION_MINT_TITLE = ' Owner and TokenMaster tokens on Mainnet'
|
||||
OWNER_TOKEN_NAME = 'Owner-'
|
||||
MASTER_TOKEN_NAME = 'TMaster-'
|
||||
OWNER_TOKEN_SYMBOL = 'OWN'
|
||||
MASTER_TOKEN_SYMBOL = 'TM'
|
||||
TOAST_AIRDROPPING_TOKEN_1 = 'Airdropping '
|
||||
TOAST_AIRDROPPING_TOKEN_2 = ' Owner token to you...'
|
||||
TOAST_TOKENS_BEING_MINTED = ' Owner and TokenMaster tokens are being minted...'
|
||||
|
||||
|
||||
class AirdropsElements(Enum):
|
||||
WELCOME_TITLE = "Airdrop community tokens"
|
||||
WELCOME_SUBTITLE = 'You can mint custom tokens and collectibles for your community'
|
||||
WELCOME_CHECKLIST_ELEMENT_1 = 'Reward individual members with custom tokens for their contribution'
|
||||
WELCOME_CHECKLIST_ELEMENT_2 = 'Incentivise joining, retention, moderation and desired behaviour'
|
||||
WELCOME_CHECKLIST_ELEMENT_3 = 'Require holding a token or NFT to obtain exclusive membership rights'
|
||||
INFOBOX_TITLE = 'Get started'
|
||||
INFOBOX_TEXT = 'In order to Mint, Import and Airdrop community tokens, you first need to mint your Owner token which will give you permissions to access the token management features for your community.'
|
||||
|
||||
|
||||
class ToastMessages(Enum):
|
||||
CREATE_PERMISSION_TOAST = 'Community permission created'
|
||||
UPDATE_PERMISSION_TOAST = 'Community permission updated'
|
||||
DELETE_PERMISSION_TOAST = 'Community permission deleted'
|
||||
KICKED_USER_TOAST = ' was kicked from '
|
||||
|
||||
|
||||
class LimitWarnings(Enum):
|
||||
MEMBER_ROLE_LIMIT_WARNING = 'Max of 5 ‘become member’ permissions for this Community has been reached. You will need to delete an existing ‘become member’ permission before you can add a new one.'
|
|
@ -0,0 +1,18 @@
|
|||
# Images paths
|
||||
PERMISSION_WELCOME_IMAGE_PATH = '/imports/assets/png/community/permissions2_3.png'
|
||||
AIRDROPS_WELCOME_IMAGE_PATH = '/imports/assets/png/community/airdrops8_1.png'
|
||||
TOKENS_WELCOME_IMAGE_PATH = '/imports/assets/png/community/mint2_1.png'
|
||||
PLUG_IN_KEYCARD_IMAGE_PATH = '/imports/assets/png/keycard/empty-reader.png'
|
||||
INSERT_KEYCARD_IMAGE_PATH = '/imports/assets/png/keycard/card_insert/img-15.png'
|
||||
EMPTY_KEYCARD_IMAGE_PATH = '/imports/assets/png/keycard/card-empty.png'
|
||||
KEYCARD_INSERTED_IMAGE_PATH = '/imports/assets/png/keycard/card-inserted.png'
|
||||
CHOOSE_KEYCARD_PIN_IMAGE_PATH = '/imports/assets/png/keycard/enter-pin-0.png'
|
||||
KEYCARD_SUCCESS_IMAGE_PATH = '/imports/assets/png/keycard/strong_success/img-20.png'
|
||||
KEYCARD_RECOGNIZED_IMAGE_PATH = '/imports/assets/png/keycard/success/img-13.png'
|
||||
KEYCARD_ERROR_IMAGE_PATH = '/imports/assets/png/keycard/plain-error.png'
|
||||
HEART_EMOJI_PATH = '/imports/assets/icons/emojiReactions/heart.svg'
|
||||
THUMBSUP_EMOJI_PATH = '/imports/assets/icons/emojiReactions/thumbsUp.svg'
|
||||
THUMBSDOWN_EMOJI_PATH = '/imports/assets/icons/emojiReactions/thumbsDown.svg'
|
||||
LAUGHING_EMOJI_PATH = '/imports/assets/icons/emojiReactions/laughing.svg'
|
||||
SAD_EMOJI_PATH = '/imports/assets/icons/emojiReactions/sad.svg'
|
||||
ANGRY_EMOJI_PATH = '/imports/assets/icons/emojiReactions/angry.svg'
|
|
@ -0,0 +1,54 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class Keycard(Enum):
|
||||
KEYCARD_PIN = '111111'
|
||||
KEYCARD_INCORRECT_PIN = '222222'
|
||||
KEYCARD_CORRECT_PUK = '111111111111'
|
||||
KEYCARD_INCORRECT_PUK = '222222222222'
|
||||
KEYCARD_NAME = 'Test Keycard'
|
||||
ACCOUNT_NAME = 'Test Account'
|
||||
KEYCARD_POPUP_HEADER_CREATE_SEED = 'Create a new Keycard account with a new seed phrase'
|
||||
KEYCARD_POPUP_HEADER_IMPORT_SEED = 'Import or restore a Keycard via a seed phrase'
|
||||
KEYCARD_POPUP_HEADER_SET_UP_EXISTING = 'Set up a new Keycard with an existing account'
|
||||
KEYCARD_INSTRUCTIONS_PLUG_IN = 'Plug in Keycard reader...'
|
||||
KEYCARD_INSTRUCTIONS_INSERT_KEYCARD = 'Insert Keycard...'
|
||||
KEYCARD_RECOGNIZED = 'Keycard recognized'
|
||||
KEYCARD_CHOOSE_PIN = 'Choose a Keycard PIN'
|
||||
KEYCARD_ENTER_PIN = "Enter this Keycard’s PIN"
|
||||
KEYCARD_ENTER_PIN_2 = "Enter Keycard PIN"
|
||||
KEYCARD_PIN_NOTE = 'It is very important that you do not lose this PIN'
|
||||
KEYCARD_REPEAT_PIN = 'Repeat Keycard PIN'
|
||||
KEYCARD_PIN_SET = 'Keycard PIN set'
|
||||
KEYCARD_PIN_VERIFIED = 'Keycard PIN verified!'
|
||||
KEYCARD_NAME_KEYCARD = 'Name this Keycard'
|
||||
KEYCARD_NAME_ACCOUNTS = 'Name accounts'
|
||||
KEYCARD_NEW_ACCOUNT_CREATED = 'New account successfully created'
|
||||
KEYCARD_READY = 'Keycard is ready to use!'
|
||||
KEYCARD_SELECT_KEYPAIR = 'Select a key pair'
|
||||
KEYCARD_SELECT_WHICH_PAIR = 'Select which key pair you’d like to move to this Keycard'
|
||||
KEYCARD_KEYPAIR_INFO = 'Moving this key pair will require you to use your Keycard to login'
|
||||
KEYCARD_MIGRATING = 'Migrating key pair to Keycard'
|
||||
KEYCARD_KEYPAIR_MIGRATED = 'Keypair successfully migrated'
|
||||
KEYCARD_COMPLETE_MIGRATION = 'To complete migration close Status and log in with your new Keycard'
|
||||
KEYCARD_EMPTY = 'Keycard is empty'
|
||||
KEYCARD_NO_KEYPAIR = 'There is no key pair on this Keycard'
|
||||
KEYCARD_NOT = 'This is not a Keycard'
|
||||
KEYCARD_NOT_RECOGNIZED_NOTE = 'The card inserted is not a recognised Keycard,\nplease remove and try and again'
|
||||
KEYCARD_LOCKED = 'Keycard locked'
|
||||
KEYCARD_LOCKED_NOTE = 'You will need to unlock it before proceeding'
|
||||
KEYCARD_ACCOUNTS = 'Accounts on this Keycard'
|
||||
KEYCARD_FACTORY_RESET_TITLE = 'A factory reset will delete the key on this Keycard.\nAre you sure you want to do this?'
|
||||
KEYCARD_FACTORY_RESET_SUCCESSFUL = 'Keycard successfully factory reset'
|
||||
KEYCARD_YOU_CAN_USE_AS_EMPTY = 'You can now use this Keycard as if it\nwas a brand new empty Keycard'
|
||||
KEYCARD_INCORRECT_PIN_MESSAGE = 'PIN incorrect'
|
||||
KEYCARD_4_ATTEMPTS_REMAINING = '4 attempts remaining'
|
||||
KEYCARD_3_ATTEMPTS_REMAINING = '3 attempts remaining'
|
||||
KEYCARD_2_ATTEMPTS_REMAINING = '2 attempts remaining'
|
||||
KEYCARD_1_ATTEMPT_REMAINING = '1 attempt remaining'
|
||||
KEYCARD_LOCKED_INCORRECT_PIN = 'Pin entered incorrectly too many times'
|
||||
KEYCARD_LOCKED_INCORRECT_PUK = 'Puk entered incorrectly too many times'
|
||||
KEYCARD_UNLOCK = 'Unlock this Keycard'
|
||||
KEYCARD_ENTER_PUK = 'Enter PUK'
|
||||
KEYCARD_UNLOCK_SUCCESSFUL = 'Unlock successful'
|
||||
KEYCARD_PUK_IS_INCORRECT = 'The PUK is incorrect, try entering it again'
|
|
@ -0,0 +1,4 @@
|
|||
# Links for link preview tests
|
||||
external_link = 'https://github.com/status-im/status-desktop/issues/12018'
|
||||
link_to_status_community = 'https://status.app/c/G4IAAMSIuU08Lm3oHzSz695ImidijVBxyoFDGEiSYAvADsk9ZVOKYlT2b-lHStyz1MqqkK2Xa4FwoUiq3LBgWsYI_ht6hWXCyLu0TGAk0dGu8IyQWtDSdIXOQ3hWscLjkTo5Vg5-eyUuV8jOVv7khJ_uTofT_TijN-sB#zQ3shZeEJqTC1xhGUjxuS4rtHSrhJ8vUYp64v6qWkLpvdy9L9'
|
||||
status_user_profile_link = 'https://status.app/u/iwWACgoKCHNxdWlzaGVyAw==#zQ3shvMPZSyaUbjBjaNpNP1bPGsGpQDp59dZ4Gmz7UEy5o791'
|
|
@ -0,0 +1,14 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class Messaging(Enum):
|
||||
WELCOME_GROUP_MESSAGE = "Welcome to the beginning of the "
|
||||
CONTACT_REQUEST_SENT = 'Contact Request Sent'
|
||||
NO_FRIENDS_ITEM = 'You don’t have any contacts yet'
|
||||
NEW_CONTACT_REQUEST = 'New Contact Request'
|
||||
MESSAGE_NOTE_IDENTITY_REQUEST = 'Ask a question only they can answer'
|
||||
YOU_NEED_TO_BE_A_MEMBER = 'You need to be a member of this group to send messages'
|
||||
ID_VERIFICATION_REQUEST_SENT = 'ID verification request sent'
|
||||
ID_VERIFICATION_REPLY_SENT = 'ID verification reply sent'
|
||||
SHOW_PREVIEWS_TITLE = 'Show link previews?'
|
||||
SHOW_PREVIEWS_TEXT = 'A preview of your link will be shown here before you send it'
|
|
@ -0,0 +1,35 @@
|
|||
from collections import namedtuple
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class OnboardingMessages(Enum):
|
||||
WRONG_LOGIN_LESS_LETTERS = 'Display Names must be at least 5 character(s) long'
|
||||
WRONG_LOGIN_SYMBOLS_NOT_ALLOWED = 'Invalid characters (use A-Z and 0-9, hyphens and underscores only)'
|
||||
WRONG_PASSWORD = 'Password must be at least 10 characters long'
|
||||
PASSWORDS_DONT_MATCH = "Passwords don't match"
|
||||
PASSWORD_INCORRECT = 'Password incorrect'
|
||||
|
||||
|
||||
class OnboardingScreensHeaders(Enum):
|
||||
YOUR_EMOJIHASH_AND_IDENTICON_RING_SCREEN_TITLE = 'Your emojihash and identicon ring'
|
||||
YOUR_PROFILE_SCREEN_TITLE = 'Your profile'
|
||||
|
||||
|
||||
class KeysExistText(Enum):
|
||||
KEYS_EXIST_TITLE = 'Keys for this account already exist'
|
||||
KEYS_EXIST_TEXT = (
|
||||
"Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase. In case of Keycard try recovering using PUK or reinstall the app and try login with the Keycard option.")
|
||||
|
||||
|
||||
password_strength_elements = namedtuple('Password_Strength_Elements',
|
||||
['strength_indicator', 'strength_color', 'strength_messages'])
|
||||
very_weak_lower_elements = password_strength_elements('Very weak', '#ff2d55', ['• Lower case'])
|
||||
very_weak_upper_elements = password_strength_elements('Very weak', '#ff2d55', ['• Upper case'])
|
||||
very_weak_numbers_elements = password_strength_elements('Very weak', '#ff2d55', ['• Numbers'])
|
||||
very_weak_symbols_elements = password_strength_elements('Very weak', '#ff2d55', ['• Symbols'])
|
||||
weak_elements = password_strength_elements('Weak', '#fe8f59', ['• Numbers', '• Symbols'])
|
||||
so_so_elements = password_strength_elements('So-so', '#ffca0f', ['• Lower case', '• Numbers', '• Symbols'])
|
||||
good_elements = password_strength_elements('Good', '#9ea85d',
|
||||
['• Lower case', '• Upper case', '• Numbers', '• Symbols'])
|
||||
great_elements = password_strength_elements('Great', '#4ebc60',
|
||||
['• Lower case', '• Upper case', '• Numbers', '• Symbols'])
|
|
@ -0,0 +1,5 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class PasswordView(Enum):
|
||||
RESTART_STATUS = 'Restart Status'
|
|
@ -0,0 +1,3 @@
|
|||
# List of social links
|
||||
social_links = ['testerTwitter', 'status.im', 'testerGithub', 'testerTube', 'testerDiscord', 'testerTelegram',
|
||||
'customLink', 'https://status.im/']
|
|
@ -0,0 +1,9 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class SyncingSettings(Enum):
|
||||
SYNC_A_NEW_DEVICE_INSTRUCTIONS_HEADER = 'Sync a New Device'
|
||||
SYNC_A_NEW_DEVICE_INSTRUCTIONS_SUBTITLE = 'You own your data. Sync it among your devices.'
|
||||
SYNC_CODE_IS_WRONG_TEXT = 'This does not look like a sync code'
|
||||
SYNC_SETUP_ERROR_PRIMARY = 'Failed to generate sync code'
|
||||
SYNC_SETUP_ERROR_SECONDARY = 'Failed to start pairing server'
|
|
@ -0,0 +1,30 @@
|
|||
"""
|
||||
Tesseract provides various configuration parameters that can be used to customize the OCR process. These parameters are passed as command-line arguments to Tesseract through the --oem and --psm options or through the config parameter in pytesseract. Here are some commonly used Tesseract configuration parameters:
|
||||
|
||||
--oem (OCR Engine Mode): This parameter specifies the OCR engine mode to use. The available options are:
|
||||
|
||||
0: Original Tesseract only.
|
||||
1: Neural nets LSTM only.
|
||||
2: Tesseract + LSTM.
|
||||
3: Default, based on what is available.
|
||||
--psm (Page Segmentation Mode): This parameter defines the page layout analysis mode to use. The available options are:
|
||||
|
||||
0: Orientation and script detection (OSD) only.
|
||||
1: Automatic page segmentation with OSD.
|
||||
2: Automatic page segmentation, but no OSD or OCR.
|
||||
3: Fully automatic page segmentation, but no OSD. (Default)
|
||||
4: Assume a single column of text of variable sizes.
|
||||
5: Assume a single uniform block of vertically aligned text.
|
||||
6: Assume a single uniform block of text.
|
||||
7: Treat the image as a single text line.
|
||||
8: Treat the image as a single word.
|
||||
9: Treat the image as a single word in a circle.
|
||||
10: Treat the image as a single character.
|
||||
--lang (Language): This parameter specifies the language(s) to use for OCR. Multiple languages can be specified separated by plus (+) signs. For example, --lang eng+fra for English and French.
|
||||
|
||||
--tessdata-dir (Tessdata Directory): This parameter sets the path to the directory containing Tesseract's language data files.
|
||||
|
||||
These are just a few examples of the commonly used configuration parameters in Tesseract. There are many more options available for advanced customization and fine-tuning of OCR results. You can refer to the official Tesseract documentation for a comprehensive list of configuration parameters and their descriptions: https://tesseract-ocr.github.io/tessdoc/Command-Line-Usage.html
|
||||
"""
|
||||
|
||||
text_on_profile_image = r'--oem 3 --psm 10'
|
|
@ -0,0 +1,96 @@
|
|||
import random
|
||||
import string
|
||||
from collections import namedtuple
|
||||
|
||||
import configs
|
||||
|
||||
UserAccount = namedtuple('User', ['name', 'password', 'seed_phrase', 'status_address'])
|
||||
user_account_one = UserAccount('squisher', '0000000000', [
|
||||
'rail', 'witness', 'era', 'asthma', 'empty', 'cheap', 'shed', 'pond', 'skate', 'amount', 'invite', 'year'
|
||||
], '0x3286c371ef648fe6232324b27ee0515f4ded24d9')
|
||||
user_account_two = UserAccount('athletic', '0000000000', [
|
||||
'measure', 'cube', 'cousin', 'debris', 'slam', 'ignore', 'seven', 'hat', 'satisfy', 'frown', 'casino', 'inflict'
|
||||
], '0x99C096bB5F12bDe37DE9dbee8257Ebe2a5667C46')
|
||||
user_account_three = UserAccount('nervous', '0000000000', [], '')
|
||||
|
||||
# users for group chat test
|
||||
group_chat_user_1 = UserAccount('group_chat_user_1', '77_80Y+2Eh', [
|
||||
'trophy', 'math', 'robust', 'lake', 'extend', 'cabbage', 'bicycle', 'begin', 'either', 'car', 'race', 'cousin'], '0xcd488381c1664c9585b7940f1c4b20f884b8b4a9')
|
||||
group_chat_user_2 = UserAccount('group_chat_user_2', '521/97Qv\:', [
|
||||
'opera', 'great', 'open', 'sight', 'still', 'quantum', 'flight', 'torch', 'mule', 'cage', 'noise', 'horn'
|
||||
|
||||
], '0x472078f0110d0bb0dfc73389ce89d8a83c8c0502')
|
||||
group_chat_user_3 = UserAccount('group_chat_user_3', '29T\I8Cv_G', [
|
||||
'bless', 'enter', 'wet', 'foot', 'lazy', 'will', 'reform', 'enemy', 'rubber', 'void', 'journey', 'fence'
|
||||
], '0x4b04b8e22e8295d0ae3177774e4acfd0badacf09')
|
||||
|
||||
# usernames and passwords for join community test
|
||||
community_user_1 = UserAccount('community_user_1', '|Br2w547YN', [
|
||||
'skirt', 'tired', 'finger', 'dinosaur', 'equal', 'garlic', 'snap', 'tired', 'friend', 'rack', 'net', 'imitate'
|
||||
], '0x21371358f1ba09204475e87444962ea4519771e1')
|
||||
community_user_2 = UserAccount('community_user_2', 'vSq5T702_p', [
|
||||
'will', 'horn', 'tail', 'stock', 'puzzle', 'warfare', 'pledge', 'uniform', 'ozone', 'taste', 'someone', 'silk'
|
||||
], '0x935034600f2ba486324cee6ae3f96ad8c8915ac6')
|
||||
|
||||
user_with_random_attributes_1 = UserAccount(
|
||||
''.join((random.choice(
|
||||
string.ascii_letters + string.digits + random.choice('_- '))
|
||||
for i in range(5, 25))
|
||||
).strip(' '),
|
||||
''.join((random.choice(
|
||||
string.ascii_letters + string.digits + string.punctuation)
|
||||
for _ in range(10, 28))
|
||||
), [], ''
|
||||
)
|
||||
|
||||
user_with_random_attributes_2 = UserAccount(
|
||||
''.join((random.choice(
|
||||
string.ascii_letters + string.digits + random.choice('_- '))
|
||||
for i in range(5, 25))
|
||||
).strip(' '),
|
||||
''.join((random.choice(
|
||||
string.ascii_letters + string.digits + string.punctuation)
|
||||
for _ in range(10, 28))
|
||||
), [], ''
|
||||
)
|
||||
|
||||
user_account_one_changed_password = UserAccount('squisher', 'NewPassword@12345', [], '')
|
||||
user_account_one_changed_name = UserAccount('NewUserName', '0000000000', [], '')
|
||||
|
||||
user_with_funds = UserAccount('User_with_funds', '0000000000', [
|
||||
'vocal', 'fruit', 'ordinary', 'meadow', 'south', 'athlete', 'inherit', 'since', 'version', 'pitch', 'oppose',
|
||||
'lonely'
|
||||
], '0x26d6e10a6af4eb4d12ff4cf133a843eb4fa88d0b')
|
||||
|
||||
community_params = {
|
||||
'name': ''.join(random.choices(string.ascii_letters +
|
||||
string.digits, k=30)),
|
||||
'description': ''.join(random.choices(string.ascii_letters +
|
||||
string.digits, k=140)),
|
||||
'logo': {'fp': configs.testpath.TEST_IMAGES / 'comm_logo.jpeg', 'zoom': None, 'shift': None},
|
||||
'banner': {'fp': configs.testpath.TEST_IMAGES / 'comm_banner.jpeg', 'zoom': None, 'shift': None},
|
||||
'intro': ''.join(random.choices(string.ascii_letters +
|
||||
string.digits, k=200)),
|
||||
'outro': ''.join(random.choices(string.ascii_letters +
|
||||
string.digits, k=80))
|
||||
}
|
||||
|
||||
UserCommunityInfo = namedtuple('CommunityInfo', ['name', 'description', 'members', 'image'])
|
||||
UserChannel = namedtuple('Channel', ['name', 'selected', 'visible'])
|
||||
|
||||
account_list_item = namedtuple('AccountListItem', ['name', 'color', 'emoji'])
|
||||
wallet_account_list_item = namedtuple('WalletAccountListItem', ['name', 'icon_color', 'icon_emoji', 'object'])
|
||||
|
||||
account_list_item_2 = namedtuple('AccountListItem', ['name2', 'color2', 'emoji2'])
|
||||
wallet_account_list_item_2 = namedtuple('WalletAccountListItem', ['name', 'icon', 'object'])
|
||||
|
||||
wallet_account = namedtuple('PrivateKeyAddressPair', ['private_key', 'wallet_address'])
|
||||
private_key_address_pair_1 = wallet_account('2daa36a3abe381a9c01610bf10fda272fbc1b8a22179a39f782c512346e3e470',
|
||||
'0xd89b48cbcb4244f84a4fb5d3369c120e8f8aa74e')
|
||||
|
||||
token_list_item = namedtuple('TokenListItem', ['title', 'object'])
|
||||
|
||||
ens_user_name = ''.join(
|
||||
random.choices(string.digits + string.ascii_lowercase, k=8))
|
||||
|
||||
community_tags = ['Activism', 'Art', 'Blockchain', 'Books & blogs', 'Career', 'Collaboration', 'Commerce', 'Culture', 'DAO', 'DIY', 'DeFi', 'Design', 'Education', 'Entertainment', 'Environment', 'Ethereum', 'Event', 'Fantasy', 'Fashion', 'Food', 'Gaming', 'Global', 'Health', 'Hobby', 'Innovation', 'Language', 'Lifestyle', 'Local', 'Love', 'Markets', 'Movies & TV', 'Music', 'NFT', 'NSFW', 'News', 'Non-profit', 'Org', 'Pets', 'Play', 'Podcast', 'Politics', 'Privacy', 'Product', 'Psyche', 'Security', 'Social', 'Software dev', 'Sports', 'Tech', 'Travel', 'Vehicles', 'Web3']
|
|
@ -0,0 +1,77 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class DerivationPath(Enum):
|
||||
CUSTOM = 'Custom'
|
||||
ETHEREUM = 'Ethereum'
|
||||
ETHEREUM_ROPSTEN = 'Ethereum Testnet (Ropsten)'
|
||||
ETHEREUM_LEDGER = 'Ethereum (Ledger)'
|
||||
ETHEREUM_LEDGER_LIVE = 'Ethereum (Ledger Live/KeepKey)'
|
||||
STATUS_ACCOUNT_DERIVATION_PATH = "m / 44' / 60' / 0' / 0 / 0"
|
||||
GENERATED_ACCOUNT_DERIVATION_PATH_1 = "m / 44' / 60' / 0' / 0 / 1"
|
||||
|
||||
|
||||
class WalletNetworkSettings(Enum):
|
||||
EDIT_NETWORK_LIVE_TAB = 'Live Network'
|
||||
EDIT_NETWORK_TEST_TAB = 'Test Network'
|
||||
TESTNET_SUBTITLE = 'Switch entire Status app to testnet only mode'
|
||||
TESTNET_ENABLED_TOAST_MESSAGE = 'Testnet mode turned on'
|
||||
TESTNET_DISABLED_TOAST_MESSAGE = 'Testnet mode turned off'
|
||||
ACKNOWLEDGMENT_CHECKBOX_TEXT = ('I understand that changing network settings can cause unforeseen issues, errors, '
|
||||
'security risks and potentially even loss of funds.')
|
||||
REVERT_TO_DEFAULT_LIVE_MAINNET_TOAST_MESSAGE = 'Live network settings for Mainnet reverted to default'
|
||||
REVERT_TO_DEFAULT_TEST_MAINNET_TOAST_MESSAGE = 'Test network settings for Mainnet reverted to default'
|
||||
STATUS_ACCOUNT_DEFAULT_NAME = 'Account 1'
|
||||
STATUS_ACCOUNT_DEFAULT_COLOR = '#2a4af5'
|
||||
|
||||
|
||||
class WalletAccountSettings(Enum):
|
||||
STATUS_ACCOUNT_ORIGIN = 'Derived from your default Status keypair'
|
||||
WATCHED_ADDRESS_ORIGIN = 'Watched address'
|
||||
STORED_ON_DEVICE = 'On device'
|
||||
WATCHED_ADDRESSES_KEYPAIR_LABEL = 'Watched addresses'
|
||||
|
||||
|
||||
class WalletNetworkNaming(Enum):
|
||||
LAYER1_ETHEREUM = 'Mainnet'
|
||||
LAYER2_OPTIMISIM = 'Optimism'
|
||||
LAYER2_ARBITRUM = 'Arbitrum'
|
||||
ETHEREUM_MAINNET_NETWORK_ID = 1
|
||||
ETHEREUM_SEPOLIA_NETWORK_ID = 11155111
|
||||
OPTIMISM_MAINNET_NETWORK_ID = 10
|
||||
OPTIMISM_SEPOLIA_NETWORK_ID = 11155420
|
||||
ARBITRUM_MAINNET_NETWORK_ID = 42161
|
||||
ARBITRUM_SEPOLIA_NETWORK_ID = 421614
|
||||
|
||||
|
||||
class WalletNetworkDefaultValues(Enum):
|
||||
ETHEREUM_LIVE_MAIN = 'https://eth-archival.rpc.grove.city'
|
||||
ETHEREUM_TEST_MAIN = 'https://sepolia-archival.rpc.grove.city'
|
||||
ETHEREUM_LIVE_FAILOVER = 'https://mainnet.infura.io'
|
||||
ETHEREUM_TEST_FAILOVER = 'https://sepolia.infura.io'
|
||||
|
||||
|
||||
class WalletEditNetworkErrorMessages(Enum):
|
||||
PINGUNSUCCESSFUL = 'RPC appears to be either offline or this is not a valid JSON RPC endpoint URL'
|
||||
PINGVERIFIED = 'RPC successfully reached'
|
||||
|
||||
|
||||
class WalletOrigin(Enum):
|
||||
WATCHED_ADDRESS_ORIGIN = 'New watched address'
|
||||
|
||||
|
||||
class WalletTransactions(Enum):
|
||||
TRANSACTION_PENDING_TOAST_MESSAGE = 'Transaction pending'
|
||||
|
||||
|
||||
class WalletScreensHeaders(Enum):
|
||||
WALLET_ADD_ACCOUNT_POPUP_TITLE = 'Add a new account'
|
||||
WALLET_EDIT_ACCOUNT_POPUP_TITLE = 'Edit account'
|
||||
|
||||
|
||||
class WalletRenameKeypair(Enum):
|
||||
WALLET_SUCCESSFUL_RENAMING = 'You successfully renamed your keypair\n'
|
||||
|
||||
|
||||
class WalletSeedPhrase(Enum):
|
||||
WALLET_SEED_PHRASE_ALREADY_ADDED = 'The entered seed phrase is already added'
|
|
@ -0,0 +1,26 @@
|
|||
import squishtest # noqa
|
||||
|
||||
from . import server, context, objects_access, toplevel_window, aut, mouse
|
||||
from .squish_api import *
|
||||
|
||||
imports = {module.__name__: module for module in [
|
||||
aut,
|
||||
context,
|
||||
objects_access,
|
||||
mouse,
|
||||
server,
|
||||
toplevel_window
|
||||
]}
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
if name in imports:
|
||||
return imports[name]
|
||||
try:
|
||||
return getattr(squishtest, name)
|
||||
except AttributeError:
|
||||
raise ImportError(f'Module "driver" has no attribute "{name}"')
|
||||
|
||||
|
||||
squishtest.testSettings.waitForObjectTimeout = configs.timeouts.UI_LOAD_TIMEOUT_MSEC
|
||||
squishtest.setHookSubprocesses(True)
|
|
@ -0,0 +1,149 @@
|
|||
import allure
|
||||
import logging
|
||||
import cv2
|
||||
import numpy as np
|
||||
import squish
|
||||
from PIL import ImageGrab
|
||||
|
||||
import configs
|
||||
import driver
|
||||
import shortuuid
|
||||
from datetime import datetime
|
||||
from configs.system import IS_LIN
|
||||
from driver import context
|
||||
from driver.server import SquishServer
|
||||
from gui.objects_map.names import statusDesktop_mainWindow
|
||||
from scripts.utils import system_path, local_system
|
||||
from scripts.utils.system_path import SystemPath
|
||||
from scripts.utils.wait_for_port import wait_for_port
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AUT:
|
||||
def __init__(
|
||||
self,
|
||||
app_path: system_path.SystemPath = configs.AUT_PATH,
|
||||
user_data: SystemPath = None
|
||||
):
|
||||
self.path = app_path
|
||||
self.ctx = None
|
||||
self.pid = None
|
||||
self.port = None
|
||||
self.aut_id = f'AUT_{datetime.now():%H%M%S}'
|
||||
self.app_data = configs.testpath.STATUS_DATA / f'app_{shortuuid.ShortUUID().random(length=10)}'
|
||||
if user_data is not None:
|
||||
user_data.copy_to(self.app_data / 'data')
|
||||
self.options = ''
|
||||
driver.testSettings.setWrappersForApplication(self.aut_id, ['Qt'])
|
||||
|
||||
def __str__(self):
|
||||
return type(self).__qualname__
|
||||
|
||||
def __enter__(self):
|
||||
return self.launch()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
if exc_type:
|
||||
try:
|
||||
self.attach()
|
||||
driver.waitForObjectExists(statusDesktop_mainWindow).setVisible(True)
|
||||
configs.testpath.TEST.mkdir(parents=True, exist_ok=True)
|
||||
screenshot = configs.testpath.TEST / f'{self.aut_id}.png'
|
||||
|
||||
rect = driver.object.globalBounds(driver.waitForObject(statusDesktop_mainWindow))
|
||||
img = ImageGrab.grab(
|
||||
bbox=(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height),
|
||||
xdisplay=configs.system.DISPLAY if IS_LIN else None)
|
||||
view = cv2.cvtColor(np.array(img), cv2.COLOR_BGR2RGB)
|
||||
cv2.imwrite(str(screenshot), view)
|
||||
|
||||
allure.attach(
|
||||
name=f'Screenshot on fail: {self.aut_id}',
|
||||
body=screenshot.read_bytes(),
|
||||
attachment_type=allure.attachment_type.PNG)
|
||||
except Exception as err:
|
||||
LOG.error(err)
|
||||
|
||||
self.stop()
|
||||
|
||||
def detach_context(self):
|
||||
if self.ctx is None:
|
||||
return
|
||||
squish.currentApplicationContext().detach()
|
||||
self.ctx = None
|
||||
|
||||
def kill_process(self):
|
||||
if self.pid is None:
|
||||
LOG.warning('No PID available for AUT.')
|
||||
return
|
||||
local_system.kill_process_with_retries(self.pid)
|
||||
self.pid = None
|
||||
|
||||
@allure.step('Attach Squish to Test Application')
|
||||
def attach(self, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC):
|
||||
LOG.info('Attaching to AUT: localhost:%d', self.port)
|
||||
|
||||
try:
|
||||
SquishServer().add_attachable_aut(self.aut_id, self.port)
|
||||
if self.ctx is None:
|
||||
self.ctx = context.get_context(self.aut_id)
|
||||
squish.setApplicationContext(self.ctx)
|
||||
assert squish.waitFor(lambda: self.ctx.isRunning, configs.timeouts.PROCESS_TIMEOUT_SEC)
|
||||
except Exception as err:
|
||||
LOG.error('Failed to attach AUT: %s', err)
|
||||
self.stop()
|
||||
raise err
|
||||
LOG.info('Successfully attached AUT!')
|
||||
return self
|
||||
|
||||
@allure.step('Start AUT')
|
||||
def startaut(self):
|
||||
LOG.info('Launching AUT: %s', self.path)
|
||||
self.port = local_system.find_free_port(configs.squish.AUT_PORT, 100)
|
||||
command = [
|
||||
str(configs.testpath.SQUISH_DIR / 'bin/startaut'),
|
||||
'--verbose',
|
||||
f'--port={self.port}',
|
||||
str(self.path),
|
||||
f'--datadir={self.app_data}',
|
||||
f'--LOG_LEVEL={configs.testpath.LOG_LEVEL}',
|
||||
]
|
||||
try:
|
||||
with open(configs.AUT_LOG_FILE, "ab") as log:
|
||||
self.pid = local_system.execute(command, stderr=log, stdout=log)
|
||||
except Exception as err:
|
||||
LOG.error('Failed to start AUT: %s', err)
|
||||
self.stop()
|
||||
raise err
|
||||
LOG.info('Launched AUT under PID: %d', self.pid)
|
||||
return self
|
||||
|
||||
@allure.step('Close application')
|
||||
def stop(self):
|
||||
LOG.info('Stopping AUT: %s', self.path)
|
||||
self.detach_context()
|
||||
self.kill_process()
|
||||
|
||||
@allure.step("Start and attach AUT")
|
||||
def launch(self) -> 'AUT':
|
||||
self.startaut()
|
||||
self.wait()
|
||||
self.attach()
|
||||
return self
|
||||
|
||||
@allure.step('Waiting for port')
|
||||
def wait(self, timeout: int = 1, retries: int = 10):
|
||||
LOG.info('Waiting for AUT port localhost:%d...', self.port)
|
||||
try:
|
||||
wait_for_port('localhost', self.port, timeout, retries)
|
||||
except TimeoutError as err:
|
||||
LOG.error('Wait for AUT port timed out: %s', err)
|
||||
self.stop()
|
||||
raise err
|
||||
LOG.info('AUT port available!')
|
||||
|
||||
@allure.step('Restart application')
|
||||
def restart(self):
|
||||
self.stop()
|
||||
self.launch()
|
|
@ -0,0 +1,31 @@
|
|||
import logging
|
||||
import time
|
||||
|
||||
import allure
|
||||
import squish
|
||||
|
||||
import configs
|
||||
from driver.server import SquishServer
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@allure.step('Get application context of "{0}"')
|
||||
def get_context(aut_id: str):
|
||||
LOG.info('Attaching to: %s', aut_id)
|
||||
try:
|
||||
context = squish.attachToApplication(aut_id, SquishServer().host, SquishServer().port)
|
||||
if context is not None:
|
||||
LOG.info('AUT %s context found', aut_id)
|
||||
return context
|
||||
except RuntimeError as error:
|
||||
LOG.error('AUT %s context has not been found', aut_id)
|
||||
raise error
|
||||
|
||||
|
||||
@allure.step('Detaching')
|
||||
def detach():
|
||||
for ctx in squish.applicationContextList():
|
||||
ctx.detach()
|
||||
assert squish.waitFor(lambda: not ctx.isRunning, configs.timeouts.APP_LOAD_TIMEOUT_MSEC)
|
||||
LOG.info('All AUTs detached')
|
|
@ -0,0 +1,44 @@
|
|||
import time
|
||||
|
||||
import squish
|
||||
|
||||
|
||||
def move(obj: object, x: int, y: int, dx: int, dy: int, step: int, sleep: float = 0):
|
||||
while True:
|
||||
if x > dx:
|
||||
x -= step
|
||||
if x < x:
|
||||
x = dx
|
||||
elif x < dx:
|
||||
x += step
|
||||
if x > dx:
|
||||
x = dx
|
||||
if y > dy:
|
||||
y -= step
|
||||
if y < dy:
|
||||
y = dy
|
||||
elif y < dy:
|
||||
y += step
|
||||
if y > dy:
|
||||
y = dy
|
||||
squish.mouseMove(obj, x, y)
|
||||
time.sleep(sleep)
|
||||
if x == dx and y == dy:
|
||||
break
|
||||
|
||||
|
||||
def press_and_move(
|
||||
obj,
|
||||
x: int,
|
||||
y: int,
|
||||
dx: int,
|
||||
dy: int,
|
||||
mouse: int = squish.MouseButton.LeftButton,
|
||||
step: int = 1,
|
||||
sleep: float = 0
|
||||
):
|
||||
squish.mouseMove(obj, x, y)
|
||||
squish.mousePress(obj, x, y, mouse)
|
||||
move(obj, x, y, dx, dy, step, sleep)
|
||||
squish.mouseRelease(mouse)
|
||||
time.sleep(1)
|
|
@ -0,0 +1,32 @@
|
|||
import logging
|
||||
import time
|
||||
|
||||
import object
|
||||
import squish
|
||||
|
||||
import configs
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def walk_children(parent, depth: int = 1000):
|
||||
for child in object.children(parent):
|
||||
yield child
|
||||
if depth:
|
||||
yield from walk_children(child, depth - 1)
|
||||
|
||||
|
||||
def wait_for_template(
|
||||
real_name_template: dict, value: str, attr_name: str, timeout_sec: int = configs.timeouts.UI_LOAD_TIMEOUT_SEC):
|
||||
started_at = time.monotonic()
|
||||
while True:
|
||||
for obj in squish.findAllObjects(real_name_template):
|
||||
values = []
|
||||
if hasattr(obj, attr_name):
|
||||
current_value = str(getattr(obj, attr_name))
|
||||
if current_value == value:
|
||||
return obj
|
||||
values.append(current_value)
|
||||
if time.monotonic() - started_at > timeout_sec:
|
||||
raise RuntimeError(f'Value not found in: {values}')
|
||||
time.sleep(1)
|
|
@ -0,0 +1,69 @@
|
|||
import logging
|
||||
import typing
|
||||
|
||||
import configs.testpath
|
||||
from scripts.utils import local_system
|
||||
from scripts.utils.wait_for_port import wait_for_port
|
||||
|
||||
_PROCESS_NAME = '_squishserver'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SquishServer:
|
||||
__instance = None
|
||||
path = configs.testpath.SQUISH_DIR / 'bin' / 'squishserver'
|
||||
config = configs.testpath.ROOT / 'squish.ini'
|
||||
host = '127.0.0.1'
|
||||
port = None
|
||||
pid = None
|
||||
|
||||
def __new__(cls):
|
||||
if not SquishServer.__instance:
|
||||
SquishServer.__instance = super(SquishServer, cls).__new__(cls)
|
||||
return SquishServer.__instance
|
||||
|
||||
@classmethod
|
||||
def start(cls):
|
||||
cls.port = local_system.find_free_port(configs.squish.SERVER_PORT, 100)
|
||||
LOG.info('Starting Squish Server on port: %d', cls.port)
|
||||
cmd = [
|
||||
str(cls.path),
|
||||
'--verbose',
|
||||
f'--configfile={cls.config}',
|
||||
f'--host={cls.host}',
|
||||
f'--port={cls.port}',
|
||||
]
|
||||
with open(configs.SQUISH_LOG_FILE, "ab") as log:
|
||||
cls.pid = local_system.execute(cmd, stderr=log, stdout=log)
|
||||
|
||||
@classmethod
|
||||
def stop(cls):
|
||||
if cls.pid is None:
|
||||
return
|
||||
LOG.info('Stopping Squish Server with PID: %d', cls.pid)
|
||||
local_system.kill_process_with_retries(cls.pid)
|
||||
cls.pid = None
|
||||
cls.port = None
|
||||
|
||||
@classmethod
|
||||
def wait(cls, timeout: int = 1, retries: int = 10):
|
||||
LOG.info('Waiting for Squish server port %s:%d...', cls.host, cls.port)
|
||||
wait_for_port(cls.host, cls.port, timeout, retries)
|
||||
LOG.info('Squish server port available!')
|
||||
|
||||
# https://doc-snapshots.qt.io/squish/cli-squishserver.html
|
||||
@classmethod
|
||||
def configuring(cls, action: str, options: typing.Union[int, str, list]):
|
||||
LOG.info('Configuring Squish server config: %s', cls.config)
|
||||
cmd = [
|
||||
str(cls.path),
|
||||
f'--configfile={cls.config}',
|
||||
f'--config={action}',
|
||||
] + options
|
||||
with open(configs.SQUISH_LOG_FILE, "ab") as log:
|
||||
rval = local_system.run(cmd, stdout=log, stderr=log)
|
||||
|
||||
@classmethod
|
||||
def add_attachable_aut(cls, aut_id: str, port: int):
|
||||
cls.configuring('addAttachableAUT', [aut_id, f'localhost:{port}'])
|
|
@ -0,0 +1,17 @@
|
|||
import time
|
||||
|
||||
import configs.timeouts
|
||||
import driver
|
||||
|
||||
|
||||
def waitFor(condition, timeout_msec: int = configs.timeouts.UI_LOAD_TIMEOUT_MSEC) -> bool:
|
||||
started_at = time.monotonic()
|
||||
while not condition():
|
||||
time.sleep(1)
|
||||
if time.monotonic() - started_at > timeout_msec / 1000:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def isFrozen(timeout_msec):
|
||||
return driver.currentApplicationContext().isFrozen(timeout_msec)
|
|
@ -0,0 +1,52 @@
|
|||
import squish
|
||||
import toplevelwindow
|
||||
|
||||
import configs
|
||||
|
||||
|
||||
def maximize(object_name):
|
||||
def _maximize() -> bool:
|
||||
try:
|
||||
toplevelwindow.ToplevelWindow(squish.waitForObject(object_name)).maximize()
|
||||
return True
|
||||
except RuntimeError:
|
||||
return False
|
||||
|
||||
return squish.waitFor(lambda: _maximize(), configs.timeouts.UI_LOAD_TIMEOUT_MSEC)
|
||||
|
||||
|
||||
def minimize(object_name):
|
||||
def _minimize() -> bool:
|
||||
try:
|
||||
toplevelwindow.ToplevelWindow(squish.waitForObject(object_name)).minimize()
|
||||
return True
|
||||
except RuntimeError:
|
||||
return False
|
||||
|
||||
return squish.waitFor(lambda: _minimize(), configs.timeouts.UI_LOAD_TIMEOUT_MSEC)
|
||||
|
||||
|
||||
def set_focus(object_name):
|
||||
def _set_focus() -> bool:
|
||||
try:
|
||||
toplevelwindow.ToplevelWindow(squish.waitForObject(object_name)).setFocus()
|
||||
return True
|
||||
except RuntimeError:
|
||||
return False
|
||||
|
||||
return squish.waitFor(lambda: _set_focus(), configs.timeouts.UI_LOAD_TIMEOUT_MSEC)
|
||||
|
||||
|
||||
def on_top_level(object_name):
|
||||
def _on_top() -> bool:
|
||||
try:
|
||||
toplevelwindow.ToplevelWindow(squish.waitForObject(object_name)).setForeground()
|
||||
return True
|
||||
except RuntimeError:
|
||||
return False
|
||||
|
||||
return squish.waitFor(lambda: _on_top(), configs.timeouts.UI_LOAD_TIMEOUT_MSEC)
|
||||
|
||||
|
||||
def close(object_name):
|
||||
squish.sendEvent("QCloseEvent", squish.waitForObject(object_name))
|
|
@ -0,0 +1,22 @@
|
|||
[
|
||||
{
|
||||
"name": "Skipped tests",
|
||||
"matchedStatuses": ["skipped"]
|
||||
},
|
||||
{
|
||||
"name": "Lookup errors",
|
||||
"messageRegex": ".*LookupError.*"
|
||||
},
|
||||
{
|
||||
"name": "Lost connection",
|
||||
"messageRegex": ".*Lost connection to AUT.*"
|
||||
},
|
||||
{
|
||||
"name": "Connection refused",
|
||||
"messageRegex": ".*connection to AUT refused.*"
|
||||
},
|
||||
{
|
||||
"name": "Assertion errors",
|
||||
"messageRegex": ".*AssertionError.*"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,2 @@
|
|||
os_platform = linux
|
||||
python_version = Python 3.10
|
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 759 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 8.0 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 2.4 MiB |
After Width: | Height: | Size: 2.8 MiB |
After Width: | Height: | Size: 750 KiB |
|
@ -0,0 +1 @@
|
|||
abc5e7b79c460d18d465d9865b45ad851d9d705c5cbdc3bf4e39bcf108c54a13
|
|
@ -0,0 +1 @@
|
|||
MANIFEST-000000
|
|
@ -0,0 +1,8 @@
|
|||
=============== Mar 27, 2024 (+03) ===============
|
||||
12:46:16.766171 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
12:46:16.778995 db@open opening
|
||||
12:46:16.780441 version@stat F·[] S·0B[] Sc·[]
|
||||
12:46:16.784239 db@janitor F·2 G·0
|
||||
12:46:16.784254 db@open done T·5.241125ms
|
||||
13:11:42.095155 db@close closing
|
||||
13:11:42.095940 db@close done T·897.417µs
|
|
@ -0,0 +1 @@
|
|||
MANIFEST-000000
|
|
@ -0,0 +1,8 @@
|
|||
=============== Mar 27, 2024 (+03) ===============
|
||||
12:46:16.575560 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
12:46:16.589620 db@open opening
|
||||
12:46:16.590228 version@stat F·[] S·0B[] Sc·[]
|
||||
12:46:16.594453 db@janitor F·2 G·0
|
||||
12:46:16.594614 db@open done T·4.981291ms
|
||||
13:11:42.096575 db@close closing
|
||||
13:11:42.096725 db@close done T·152.167µs
|
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Gloves Off!"
|
||||
:author "BLANK"
|
||||
:thumbnail "e30101701220da875bea9be16e26b6a94ddc17282db7d182e0b9071c21bbc08577c99670cea2"
|
||||
:preview "e301017012205227cae4e3678084fe36a1fffdd570bb676a7931924f60798ad1cd6d8c052a8f"
|
||||
:stickers [{:hash "e301017012208ef176695b470783c4ae41693bdae673b2f8e28176216564d3af7b30f285817d"}{:hash "e30101701220c4e1053672d055e7614cbdcfc176256f2e2fa1d98a0cefe574f8cead03ee2722"}{:hash "e3010170122084f7535d14ce0ba5887615cd6fc135555a936cd549963cbf7222e23d35f75ace"}{:hash "e301017012205e9601ad838858f600f0aaca35615cba180d457b4833a26de79583dae9589e65"}{:hash "e301017012209e13be35abd1d83ad460c7d190eb1d94c13c081a8cd7b183ba001db58ccd1154"}{:hash "e301017012200460ab75ced426e48fd2fcf43d619268aa0effbd65ce84a4e03dee3f89fa85fd"}{:hash "e30101701220cb4a558971a4200e5b5f3ea67e822264d57c113e46fc44367b72f50cc01203c7"}{:hash "e301017012208535e0ff656c5a2dd0093aacd745b718e4315f2731f35de1f10fdd8ada1db27b"}{:hash "e30101701220c55bb90fe9e7f56f0927f4215048c4ff356eca744fdd435cc6fbb3e346a7fed4"}{:hash "e30101701220525bf59df45390098a4b38d365bad872580eb22ce290c24e13080e07ca2147b1"}{:hash "e30101701220e1052919f334506d398219ab658580b585d4ae824b0cc9ecf3aba0d896668444"}{:hash "e301017012209e17bcf843e2deb433d5d4eff9655f41cc1b7b2972c80655fe600f5e57b2a76d"}{:hash "e30101701220dfc066b094f3a76e5c71d951c8f8325a4449ed712de0a2bd171985377039a52b"}{:hash "e3010170122003e19ed9fe44e5b19d6b825b141161b756529a7821c7dc2505365e68dd6f2e8f"}{:hash "e30101701220be65a820bee00873aed5dcbe6434beccc91c10d0370f2d6b73871feb492eeb5a"}{:hash "e301017012205dca7ba03997692672fc4a9d4c6363859fa66f5b59a4d5459fa53cd2d2f5a048"}{:hash "e30101701220d835cf64a25587af237e9f4d429b00cb3ce3e6c1034f6eaeac2a0dcb9c6d9919"}{:hash "e301017012207baa0f8212ede4b59913e53070a74945b90ea9b8f3201ae391dd1b3d75d6bddb"}{:hash "e301017012205c9d1f7e11f3962076e6d202ef2ba7361ce668f9710ce09da30ac2526b0470dd"}]}}
|
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Flex Dapps"
|
||||
:author "Flex Dapps"
|
||||
:thumbnail "e30101701220873700df21773072d422b6026b21e9c92223abca3b394d569cfbdf9061123cd4"
|
||||
:preview "e301017012201d145070ed2205a378dbc8a6093a2d54a7b4ba2ac8d2e2f8aba682fa06ee7138"
|
||||
:stickers [{:hash "e3010170122074ae324e29e253897333273224609092d4b31f68d28f24fd5273c8ab6952d3b1"}{:hash "e301017012207d5c34ad915ddba8cea762c9b287a9705618cf6f22e3dd7eb3a43711fdeda0c5"}{:hash "e30101701220d09202c0a43e2e1b46ef750c194372441ed3abe7b24981996c2d0e03cd70c888"}{:hash "e301017012209de39eed057861d52c7f048eea091823066e17dd56d1e1bdfea5c3ea55471eb9"}{:hash "e30101701220539de8ab5bb31a14f1a01be45b25affec70071eefefe36519cced22b8cc524bb"}{:hash "e301017012206b47e20800651e5b2454597fb5bfd912733504a8b6392dd531892bd1326feb3c"}{:hash "e301017012204527a61f5725a50481fb0d8e8c790efc19486511f7d72e6fb82725e5bc60529b"}{:hash "e30101701220379255c6d42656a63c67f3bea9bd59a3aa7088d3a60515b75533d2ba8dcb5e34"}{:hash "e301017012202a7a01d770dd6bd63109b3d78ee915e21213f493639b3f7ac6272ad9bfba1808"}{:hash "e30101701220ab4d8ffa013110e163c9544f5c95f0ab3e858b4cdba1484bb39d139b58f9b116"}{:hash "e30101701220368a1ccbb28087eb8f8892958217dd31fc289866bfcc5989ac073a370dee9b98"}{:hash "e30101701220188847d73155996a7e74259e7e4daee91395696547f7b68511c079b25ea0283f"}{:hash "e3010170122010c381bbaf174ea70b51ceb1b13b5731bbea0614396fb7ecdfffd317decf701b"}{:hash "e301017012203d33e824dac0634bf3000db005e2706bbd617d715984c572c23c16a19898f7fe"}{:hash "e30101701220163e139af650225a84bd0b8028e2dc47f1ddc29fc7c817aaec021a7ce55ab306"}{:hash "e30101701220556d1403794f40495d299f3dff30e712be3dce04cd99538d440c7073dda1f23a"}{:hash "e30101701220cb6c6b567c9955c61a2dac1a349206700f4edc455facd90481a6258b1c9a981f"}]}}
|
After Width: | Height: | Size: 4.7 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Geko Story"
|
||||
:author "Kazwes"
|
||||
:thumbnail "e30101701220be83d1048c6f21944e1fc91061387b38650649f9c02f8d8f97b3426fa86fb211"
|
||||
:preview "e30101701220215cf6f423ce201e6084b4778df0e2d24688757f49f991193773d690ecd79317"
|
||||
:stickers [{:hash "e30101701220bc72ef41c365f7e0e60e400e5025740ca8f82a0855861ea1d66b46246900a123"}{:hash "e30101701220603a4868b324787f4260ea2a2b1bd20b7b8ecc5962b0c3f74e49117f05a3b04b"}{:hash "e30101701220eb1f135ab6cb03beb36e097af196e4b8624bd11a8476e8ac88aa50c0732dca9f"}{:hash "e301017012200dd07d73782d9f58229d45efe784c8cff95331e1b6d210dc1e15132efdf87f3e"}{:hash "e30101701220134938d9f54f55f83358c2b8ad1e32259dfde562b5648be41df7238e70b5cbef"}{:hash "e3010170122097bcde34f4128486ddfbf7ae42c8d28a27074832bd2eed09927b98ba61f6fc32"}{:hash "e301017012202f2cc01a9f9548f53afa390bb9cbdcc4853b1045bc288e6d7f38768d83dedd49"}{:hash "e3010170122066ab31f6f1ceeb289f97b0da47197450431b37bc62e5f04806e9c2ab82e4429d"}{:hash "e301017012200351ea55cc885a90fb13b4305e6142c4c96c0d061f3c2095b776445471024a26"}{:hash "e30101701220558218d656a8d1699cd46d9eb29d12b247c0bb433a827c07b612aca97962e6e0"}{:hash "e3010170122092ac180e783922f1cd1fa82a8593f0f653c364a86b54f2931db761fa41d3192a"}{:hash "e30101701220e8d62721648fc04b05272903b4f116573d42927d015c313a18a20d2d2d375fad"}]}}
|
After Width: | Height: | Size: 35 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "HCPP20"
|
||||
:author "Melvin Alvarez"
|
||||
:thumbnail "e30101701220ee8d0e8869aa7e31490b18c6ad3e28a30e8dc06852595463e561c0e2acb6a8f1"
|
||||
:preview "e301017012202fcfa5cddf18fccfc19be1cf5e0d52c476122dc1d6b2bb31e102fb9fe8f629a0"
|
||||
:stickers [{:hash "e3010170122066ae2db8b83add4270769ffe8aece0afb79c0c29708e3f28941647268d7eb407"}{:hash "e30101701220114c763c848be25e077523216d17d3e2ccbae4a3b004179d132b7c91a2f13009"}{:hash "e30101701220daf713e4489b982338a2a823f1597edd2a0fdcf358b76375b94065ddfed50929"}{:hash "e30101701220f4aab0a4ff5a5fb2c565ab114d76dd42693302999d6392b467d6753ea9b55c12"}{:hash "e30101701220a2855135137c9c088a23658c93841f5fe47cac26983f2f19b154540665597619"}{:hash "e3010170122095912a0ff137b67465982a3fc6f8b41692e0eaeaa17ff5eaa5ed25d611c68f4f"}{:hash "e30101701220a5395eeddabb98d799031dc2bbc8bc972bf7c6f199f63003606b1c9a7348ae32"}{:hash "e30101701220fb35ff7341658353cda8d163dc979fe5844d574a3e621a761a0c9a9f163143d5"}{:hash "e301017012206328887139bd7de5148a881ae029ea75357f3e01782d01f20011ae72d7d43831"}{:hash "e30101701220e935da555834c78250fa7b2787987cc6f6db20c12dc158e63e54abd7a4cf3902"}{:hash "e3010170122058532a9d05e6352970c0c0806dd3a1ee0447866393c504713faaa0d2fd987ae8"}{:hash "e30101701220046dcd1534e099cec7ce82101ade162ab4d2cedb241f8da12fad1b7d31ddafeb"}{:hash "e30101701220277fb48b51c279960c3ff0628e726e72f9679452583aa7deb640951111c98c5a"}{:hash "e30101701220051fbd8292648d3adcefa266fe1a16713172d8364b4519f69682b55a44687eb9"}{:hash "e30101701220793880b3be9fc46eb4657bb97b5dd021ff0d8fecec3116ca48da8e6b76eb15cf"}]}}
|
After Width: | Height: | Size: 20 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Status Blue Fire"
|
||||
:author "Status"
|
||||
:thumbnail "e3010170122033099a15a215267b117fcba796aea248b6062b614a2fdae6b63d47bef808301a"
|
||||
:preview "e3010170122097f821431baaf9161d5a2f83966d4f8b76a9a50d7e2119639305cdf2cfab7a79"
|
||||
:stickers [{:hash "e30101701220d444419a474cc5151661bd15e26fc361bbe6590e99f565e0d0006f7bd2d82e8c"}{:hash "e301017012202a858cc945af30be5c1d31a5709cde4d36edf201e650a2d4f8714234d713d6c0"}{:hash "e30101701220d1a479a51bb40a9e2fa7911beb22f17da4a9b0067e365e55fc16b9d172b666f6"}{:hash "e30101701220092236e54bb771a241121c72fe40b6f3db8914b0a5e77d8e5ec0244a05bd3246"}{:hash "e30101701220ec585ffddd856cf2db7afe213161b3e1b88c127e8c2869ebe3ee6b57f0f6fb6a"}{:hash "e301017012202f1ba39269e9d9b9db48b39088996344222ea0868a1d67a6637ec05a4186c6d6"}{:hash "e30101701220655aecc888fa92b4236bb67e27001a79a43c65f7f9b636290941c28afef58e81"}{:hash "e30101701220c8afcdf82946796d7ec854b0a3202df6332712eca372adb9783d790a356dc98a"}{:hash "e30101701220f9021715c3308492cca0c314a138bca4773b432500639c2de4aa6823e12f378e"}{:hash "e301017012209bb202ddbd81c28a3aa7dbb2f6eb96fbe76c30d6dab1eb1b6c483bae7c66900e"}{:hash "e301017012209df6712fffc07507183ca222c0b60b3e002cef901dfa1b9a9d02a71886641f02"}{:hash "e301017012208231695c75e893f485816fe802a4827056dad86283708127dc4400893740a6ca"}{:hash "e30101701220bcc65ddff08c287fd115512d1833e6f19efbf3f560c7e74f9ef18cbd456dc058"}{:hash "e30101701220b1d0a8057beecfe64ef1f34208c26a2a731391fd65768121b94a456e69c916bc"}{:hash "e30101701220bd28c22baf338a3f0c3391ad7b61b726e11e1fd0b3373428e559fcbc50efd553"}{:hash "e30101701220aaa950f7516cca7bcd6d0d6bdb0485b0bae5863bb948ba13b6df4fe7d1fa9e4a"}{:hash "e30101701220ae1bf86ff5c9edeb708c3bcc76374ce23deb9e99f75f20d9442335fbd8a6705b"}{:hash "e301017012206da1a9e1b41e2fd8284f73e97516bbff037e8d81893c95d7c70871ac8a178c99"}{:hash "e3010170122003d91734ccf45195fb2a9e06ee06f718ea63720d7167e631936d75c4a59f2ccf"}{:hash "e301017012201bc272f8c809d7c318e239f27cdb2257266ba964d91d9751d7a5f23f831f4451"}{:hash "e30101701220c1b010073c69d3630c9969356fec55bf2d42c4d2efc51f0a56bd42d4a367691e"}]}}
|
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "MyCrypto Taylor"
|
||||
:author "MyCrypto"
|
||||
:thumbnail "e30101701220fe0b90aa704e2c0d01a3319ee82052aa04867937edaba0a4c921bf068379489f"
|
||||
:preview "e3010170122079f39b2454fa44968c5cfcf2523116a6bf6e4d78eb0b54beea7f9e15414cda3b"
|
||||
:stickers [{:hash "e30101701220a3c3fdee0f02fdce4f43bc19f98ae5ff63f52370ccb7eccc915c031e7148d701"}{:hash "e301017012202eab276e6dda5a68173d25a523e3d77c0a47b3d31a9fa6a74f18e0347a52d630"}{:hash "e301017012204a403f4465897868b3f95d11b9dd27b78dfb34ed252446a22f4943a1e10b7820"}{:hash "e30101701220f9e4aef4ceb0b64c05e45980a1c34114ad8a0409b63324b7d119efeb5e00da9f"}{:hash "e3010170122005ab729ca894c0e95b2a11f9a67ee97234aef88084c7fa91586682f8eb4e75db"}]}}
|
After Width: | Height: | Size: 5.3 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Infor the Techicorn"
|
||||
:author "Brooklyn Design Factory"
|
||||
:thumbnail "e30101701220965e78f8cf56c81470567ffcdbba6ed7102844db1226c5787fa169061cba8265"
|
||||
:preview "e301017012206510ff8e1d37b8e1a7f812e6436ef6dfda266c8921bcb3679bc63b1d3f4537ae"
|
||||
:stickers [{:hash "e30101701220531612eae8a2f99c0c8aca5f2c45c79a1e6b8340332d11fdefd9070c3a6f3b32"}{:hash "e301017012202524a14d6616cd6e80cd00bbd23a3a92e0b1ae820972b5387ad2f35cd8d4b036"}{:hash "e301017012202e5e03c7f1d0d3dae5d732bcbde4bdacfa2981ce0be9e51f688758b115dae60f"}{:hash "e3010170122099e65bc11a30d1900bc2d794f0fc2e2498d26f6d8b9cd1ba746219a70850a81a"}{:hash "e301017012208658ab4de74e2ea492a3a552ed86eacb60fd10592aa2a2480bc130964f5928b4"}{:hash "e30101701220e6bdb2ef306e99ba76ddce2a3382ebb0756d7973bb6807ae1f379dd03ec80e1c"}{:hash "e301017012204426c52e586dee222bb1cde8a61665b6546c7f9411ebc42aaf57c54fe3f62304"}{:hash "e3010170122001e8d85d89265c214f2481558709d050bb33e85ff7779b9650ef3a242fb9791d"}{:hash "e30101701220a253b011ba109aeccfe22ac8ea8f1366bb52fb1e3c625432202b2db837a1af1b"}{:hash "e3010170122056c9e9177acf8b0dd7d3bd12f378a35bb3d3409d3acdea24d56711b21086d73c"}{:hash "e30101701220c123bcb5f00418b589a4983b6e3f3654713c3a3ce2147c8211ec23993f0b4ce6"}{:hash "e30101701220b5044589537660ab7a7b2797278c4b2997c70eb6421fb9a83fb9aee6fb54bb98"}{:hash "e30101701220773073c867c8286916f78ef26028dea170053ab2a7382a1091bbc418bd575c45"}{:hash "e30101701220810be6f9dd3bf02a1f01746c0298c3c82555d6f227b5bf0392d394032e9a2501"}{:hash "e30101701220503df87f9ee2f5b6baaf3de76e28d775f4c9fc350235aece3cc66f18bc5742bf"}{:hash "e301017012208436337934dde644437c2075df74af6e71826509c9fbdc8b600c1b6052a2127a"}]}}
|
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Meowy Christmas"
|
||||
:author "Mindful Design Lab"
|
||||
:thumbnail "e30101701220fe33097fb023b2c8909b34296c399569321998e1b027ef6e39d47addeaac1d0a"
|
||||
:preview "e30101701220dfa41d3b1787b7a2a741d5514a0820ff2b71fedcd09c021ee74cdc8dafb629c5"
|
||||
:stickers [{:hash "e30101701220f6af4bf36e4295faa112c881e83ceeacc408dba71be326ecffd1b16c44b24899"}{:hash "e3010170122022cd017c0592de84aa4dc3659ba9463e574878fa69ef5d7518527f812e2dd9c8"}{:hash "e30101701220d1c1eba2269a27f204af9f9271a0c1d97dc861e06e9d73fcfe6988a7174c964f"}{:hash "e3010170122007d2fd8dbd76ebac7aa76f0bb5b8ce7f71a9fe8ae604f5265413f3c947cfe1c8"}{:hash "e30101701220c49f8d56d68d69052471132cd2ffdce6840b3cd5450b40905334e030a9beba70"}{:hash "e30101701220def85dae79494d0a2eb1b38b546c714e667d3d89714a1922efa8629a74e9f90b"}{:hash "e301017012207df83bdf02ba831d9dd68d079f6494fbabf86d98e9faef4cfe54cacf41f6cae3"}{:hash "e30101701220501341816ecf63a2a9d7337edb8f3090b2a694ef9771d4a49f0c4355e241ca3e"}{:hash "e30101701220fc5401c6d555fbe8fae85e4480620102bbff71afcc2234c06e52e9774293a21a"}{:hash "e30101701220d89b192fbe75648c66aa8f12fbe3164ec57363ccef829cb7ca8622dfd7f2d8ad"}{:hash "e30101701220d2ede7d91e85bc60ca46cd07728df8c0ac4ebb0350f3354b294e8bec69c8dba5"}{:hash "e3010170122029f662ff7ac94c2fcd057a786dddbd3b0a08584560d42855fbcbfab6d06f1db5"}{:hash "e30101701220a4650b468a367efbb7467537a1033285bccc5ba45c2134f5c1909ff588fc3441"}{:hash "e3010170122002c90b29e70bc935c4af8ab560e93a33ac4ff11ccb705095c5f76f2257d10c6f"}{:hash "e30101701220756641b638ac7c66c0ed615307e2312a956e47b65c04b782b61f672babe02eea"}]}}
|
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Tozemoon"
|
||||
:author "David Lanham"
|
||||
:thumbnail "e301017012209c5ba15fcb860b0715190d342dc94bd397692dc318b1b0c72f21e912904bcb93"
|
||||
:preview "e30101701220d0e9e47e06047b0bb26213ccde4bc7836199c91b3a3ab0f3fa20ac54f6e5691d"
|
||||
:stickers [{:hash "e3010170122093c41fec87274b48d07c067f478e20c132940bad57712b3215c5aba9f1862eea"}{:hash "e301017012209af36e3307f85f7c3bd741ebede2c8a41d12cd0f2b95de3c31df0a76f8783c49"}{:hash "e30101701220feb1e9c32b93efee4aa73b9f42553b789bd3eb561b0972d5841a6f16f603e89b"}{:hash "e3010170122080c27fe972a95dbb4b0ead029d2c73d18610e849fac197e91068a918755e21b2"}{:hash "e301017012203d5f53a4700324ca36d0b7dc4ab8c292be030c22578b7c9e2a199c887f35585a"}{:hash "e30101701220ffca78bc997187d454db11edc9bd4d3f1d5b53ec42a037ba39d773214cd3fc67"}{:hash "e30101701220b35bd4f43db76071ec893e1072028cdd86c72068abf5c7d2e5ff96bb8644c8c7"}{:hash "e3010170122001bbe2f5bfba0305a3bdc2047fddc47ee595a591bdee61de6040ffc2456624e1"}{:hash "e30101701220d5c71b72162a802b3cb2e3c61758d8a9780d48d78c467d160c52adc7e1e35a92"}{:hash "e301017012205225562d0790974887dabdf9d257cd808d1e470f0ab41226e1be2d522f4639af"}{:hash "e301017012206d9689241f5504021068e3c23befc859384a0e9c00c27108b76e573968f61f8c"}{:hash "e30101701220ad202b9109dd82f11782044ec443dbd56ddf16270c353704505da03f7761aabc"}{:hash "e30101701220ad95684efa3e42d4f82043210a8946745b0dd0a9b58d8f6902315047a9ee922f"}{:hash "e30101701220b91b449ec56ade3ca73fd51ac76844280cf58c01a91806e2f0417d27f55254aa"}{:hash "e3010170122036fa754dd890cbcdd66666bdc4ad18d2ce5284c8f6b6ffe2c0a091eeeba1951e"}{:hash "e30101701220b06837823a598d52584f5039882413b3616299bf32ea162c6339ada84138adc4"}{:hash "e301017012205b9b17d402f3b97fc9f43a1d389664622587b4cc109dd9fdd5ee2e8ab5ca1b7a"}{:hash "e3010170122054c748ceea79aeeb79b83bc940310ec855cab03857d60b575c4fa560572a0f11"}{:hash "e301017012205f0d53af63c96e3d778dbb47f2c57fcd417567f4c97754531f9b914cacba5e70"}{:hash "e30101701220d345e5223c5dd31c28e0d65088848e76a6144ba24c3c779d71b068f342345190"}]}}
|
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Salad Bowl"
|
||||
:author "Brooklyn Design Factory"
|
||||
:thumbnail "e30101701220f6a334d2712ef24c9b770a753a3e0c61aa35e15e485d72a7581e5fde4b4e3683"
|
||||
:preview "e30101701220ad9340c85d3ffc274eb72847f932be00f950b6ebf0062be19bba1d65a91acb93"
|
||||
:stickers [{:hash "e301017012204ae736b13b5d36c889a715687603133af304fdc03820eacf85581354d98a6712"}{:hash "e30101701220ebd80c7cb8307d64336fb9514687b71b6668ea34d46b04d6a59f4a598b5881d0"}{:hash "e301017012204d2e87e21fb8d3718f4d6c8423e98e9112544551985df9f83a4e465a701a4b48"}{:hash "e301017012203271dfc8927c3cd27c01380a6bc250faef7ce2c68c95262e031a29fa83e50cf4"}{:hash "e30101701220b4847927edbafee3f577fbc286896db789538138dc9175ec0a19036e8835756d"}{:hash "e30101701220e71c9221e01c000d8ba1043713ad1c0cdefcce49af3f1d96dddac1f054919fb6"}{:hash "e301017012203b3aa17f2aaf855a49ed56e31919f7f7ae8d053d2911aedba83e591e33b82434"}{:hash "e3010170122072c5e9dcf6b4f7adf6088158a990ccf274fb8b9b7e507038fba096b46788913b"}{:hash "e301017012202179e572ff632085b448dfe2887a36a83e495acd6fe0307a1f6668ca0709b077"}{:hash "e3010170122032463d79738d12cb02bbb0023eb16435b94b4be3490e6ddbd6118474c534b826"}{:hash "e30101701220516b38ac2eb7b680a68cde8f8e7c6d330a993a8425fb623117cacba6a3fdb8e7"}{:hash "e30101701220c0944d1daad05018198eec8f496228edad71b952953c37a5362165c2235a82d5"}{:hash "e3010170122057b680d8a46ae6435c11633e4ca7ec83f411c9863c49b6069511f4652aa5f76c"}{:hash "e30101701220d2a41d7bfa31541a65a079189114bcd63c5d21d24b33af82fbc2f3d553136f32"}{:hash "e301017012209d670fdbfb075f9e1ef27746e7a1e50386b3e246d860ef6e6c478fa1a98c97f2"}{:hash "e30101701220a0df81beaf80ba987896e03eec223fcbc5599f37daf6e60388e223cf2b93f40a"}{:hash "e3010170122091c446ed54c55c6edf7b64a1d6b5dfd92d1c95472b135e05e31a704344128def"}{:hash "e30101701220999c3d750efe949a82eeedc17165fdb8e960e13d0d2e6e4315bae3b3eed04538"}{:hash "e30101701220bed5966ef10352d9f9e0746e376884d1602dbca02a6d3fc1ef852516d1b03163"}{:hash "e30101701220a5b5f70d5ac5d0203da5146b87f278d2c88aa39d9b5ff959ef00e512b324bec5"}{:hash "e30101701220f539e23985ac3acac097a81c0ba775a6b0220df767b3844a8502f9b49902cb7f"}{:hash "e3010170122015498aed30b352f34f3900b5b6d3b303ccef50b49ceccfada8e01cf165626e0e"}{:hash "e30101701220eb4dffb576e0d82b1ed1c34147a2d9ae395c1e104462f3263738095a7d133189"}{:hash "e301017012202b3b9e0d22b4051a3b5fd3405bf03594eb64d0c2e990eee4328a13cca2e4a21d"}]}}
|
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Kyber Network"
|
||||
:author "Kyber Network"
|
||||
:thumbnail "e30101701220f772002a1c8fad7af149999d8aa943b37841bb1ae1465bc389357293f18ec261"
|
||||
:preview "e3010170122029a9705f914645b8cf9754541633fe4a38832ab650868f9254ba8ad11e8fa4fe"
|
||||
:stickers [{:hash "e3010170122072d18671b24b34a84e0851fc0477f86a37b27c2bf181fb04e561367cce87512f"}{:hash "e30101701220218d9c4f1d83974d00de9bf9328c5f479c559fb05abf9c8f7c7d674ebbf89673"}{:hash "e30101701220e6668dca7359cc780a3fa32d5a5c3cf02333a0cf8d807dd4b7fa221e0c0562b3"}{:hash "e301017012203be79845b37acf389aa174fdd83ce8c9e05c587b23daf74d6e8bf926aee67a38"}{:hash "e301017012204a2c7fd3ec2813101cf427f7d112bf5f93e0170a2358431461f25d7d3c307d62"}{:hash "e301017012203aab35d88aa1574b5078a0338b2545cddc4ee681455b38ae476f2fb587bb773c"}{:hash "e3010170122045c295700775c477fc41e2353b13185ea83ccc54246d9b4ca9b6b9d6ecc891bf"}{:hash "e30101701220b6542c321fd81495fedb926414ba56a6a70a6a8ac6493103f510ae84222f5dbb"}{:hash "e301017012204e2a3154027e1b6d54b7a6dcedfaa4549dfd7b67f99374d1c98718005a8c63a0"}{:hash "e3010170122081112d517d9c64d43074b02603ac8f32a174007f3d9448946048800f84b5ff97"}{:hash "e301017012207d2ddab443622a4f2512e9941436c8fa51639f59df8b01176713e745467d79d6"}{:hash "e3010170122058a803c65450ac31f4ae580c972a4be24882da6377435614ff7223601c034f7f"}{:hash "e30101701220a51ecab50da228e80ddf56be6cf5f514f411f9c58c609105a7ec5213b022a59c"}{:hash "e301017012206738ac4aef625648876668f6c40d59c73a17a60c9830befef12c15a4413fc180"}{:hash "e30101701220aeaf84ba7712c440b41dfe957090f8ca592279c7042c08d3287319121c4c0aff"}{:hash "e3010170122084da9a758b1583e869baa0762b6c533c0be92265cc38def2f5714a7b1847ec1d"}{:hash "e30101701220c301c4aaec50dbabc2ed47886e411d86519499b6384b86714030db68648b194f"}{:hash "e30101701220f33e11e981e287076136e471bb8943c98f42111d05867fd0e0c7b9731cb170b4"}{:hash "e30101701220f8e368e0ad5ae5148e5a6c3c3dd818c534e9523de9976cc09b06974b0295919d"}{:hash "e30101701220293f49b119ae36a77290e9a430fa2f1207cdd2530f6603edc01d4f5b2e49fd0a"}{:hash "e30101701220d08c83e029db66af708173ee3bb4909ea2553e2db0f3246edb3fef1fc2f65968"}{:hash "e3010170122069a75205ceb3f2866e831ba278ce87e9a6a42b4f6fbbdbfda87d78fc6db34021"}]}}
|
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Status Cat"
|
||||
:author "cryptoworld1373"
|
||||
:thumbnail "e30101701220e9876531554a7cb4f20d7ebbf9daef2253e6734ad9c96ba288586a9b88bef491"
|
||||
:preview "e3010170122050efc0a3e661339f31e1e44b3d15a1bf4e501c965a0523f57b701667fa90ccca"
|
||||
:stickers [{:hash "e30101701220eab9a8ef4eac6c3e5836a3768d8e04935c10c67d9a700436a0e53199e9b64d29"}{:hash "e30101701220c8f28aebe4dbbcee896d1cdff89ceeaceaf9f837df55c79125388f954ee5f1fe"}{:hash "e301017012204861f93e29dd8e7cf6699135c7b13af1bce8ceeaa1d9959ab8592aa20f05d15f"}{:hash "e301017012203ffa57a51cceaf2ce040852de3b300d395d5ba4d70e08ba993f93a25a387e3a9"}{:hash "e301017012204f2674db0bc7f7cfc0382d1d7f79b4ff73c41f5c487ef4c3bb3f3a4cf3f87d70"}{:hash "e30101701220e8d4d8b9fb5f805add2f63c1cb5c891e60f9929fc404e3bb725aa81628b97b5f"}{:hash "e301017012206fdad56fe7a2facb02dabe8294f3ac051443fcc52d67c2fbd8615eb72f9d74bd"}{:hash "e30101701220a691193cf0559905c10a3c5affb9855d730eae05509d503d71327e6c820aaf98"}{:hash "e30101701220d8004af925f8e85b4e24813eaa5ef943fa6a0c76035491b64fbd2e632a5cc2fd"}{:hash "e3010170122049f7bc650615568f14ee1cfa9ceaf89bfbc4745035479a7d8edee9b4465e64de"}{:hash "e301017012201915dc0faad8e6783aca084a854c03553450efdabf977d57b4f22f73d5c53b50"}{:hash "e301017012200b9fb71a129048c2a569433efc8e4d9155c54d598538be7f65ea26f665be1e84"}{:hash "e30101701220d37944e3fb05213d45416fa634cf9e10ec1f43d3bf72c4eb3062ae6cc4ed9b08"}{:hash "e3010170122059390dca66ba8713a9c323925bf768612f7dd16298c13a07a6b47cb5af4236e6"}{:hash "e30101701220daaf88ace8a3356559be5d6912d5d442916e3cc92664954526c9815d693dc32b"}{:hash "e301017012203ae30594fdf56d7bfd686cef1a45c201024e9c10a792722ef07ba968c83c064d"}{:hash "e3010170122016e5eba0bbd32fc1ff17d80d1247fc67432705cd85731458b52febb84fdd6408"}{:hash "e3010170122014fe2c2186cbf9d15ff61e04054fd6b0a5dbd7f365a1807f6f3d3d3e93e50875"}{:hash "e30101701220f23a7dad3ea7ad3f3553a98fb305148d285e4ebf66b427d85a2340f66d51da94"}{:hash "e3010170122047a637c6af02904a8ae702ec74b3df5fd8914df6fb11c99446a36d890beeb7ee"}{:hash "e30101701220776f1ff89f6196ae68414545f6c6a5314c35eee7406cb8591d607a2b0533cc86"}]}}
|
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Status x Mox"
|
||||
:author "Moxarra"
|
||||
:thumbnail "e30101701220a156d5c7ed6a93a986432390d0e76164d96aeecad91c86ec5e7ca60c15fd91ad"
|
||||
:preview "e301017012209a30d94e6fd902cbf241dca23adf4ec776a7ab93abdec055f840b09455fd08ce"
|
||||
:stickers [{:hash "e30101701220621944ee8fc00e006eb44b4ea2e79e05667867dd15bb72667fc2da2f7c6dd44e"}{:hash "e30101701220fd6f82006eaea295a027ad7e1fb333297876c050a119f694fe6663c393f95e08"}{:hash "e30101701220fb8171894740049965f872b841b88c337ca75100ced7cfb58bf40b130780353d"}{:hash "e301017012207f71b6b9d2016003792b809f70b8f9ad06dc1bd54bfbef172b183e900989d253"}{:hash "e3010170122002f92aa870c4054e1c09996e4c7ec2a18ca3868cd53b653685b40e6f420782b2"}{:hash "e3010170122049b3b48f4b35b11f2f4acf5d77e1291728551d61a92db0bf1870a20a4ec82164"}{:hash "e30101701220d20ff59a18df19a6a4565b877fc2126251c2c4f960c0dc47bebdf82e2bac9035"}{:hash "e3010170122016dbb039bf1107d3203c47cb31ad8cb5c31fd81951e20f9b31c73dddd666efc3"}{:hash "e3010170122040df0288ee1e4696e256949ab1d9bebf0ebe820e8df6401c42453a8465146370"}{:hash "e301017012204586040200dcfd914b642ad5ded8436a59969045682b7e96b6d0ab9d19274aaf"}]}}
|
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Foam Fingers"
|
||||
:author "Billy Osborne"
|
||||
:thumbnail "e30101701220f4e6651aa43c56c11d2fdc1c8bee85e5f7e942a1a86bf6f2a955ec703fba32a4"
|
||||
:preview "e3010170122015adad810f81450639b25fb93a945ddf9121a2bddba3c8d217f3ad28581ca560"
|
||||
:stickers [{:hash "e3010170122011341156592573b17306173544693b801c7158416dc66eab92d377d652b812e7"}{:hash "e301017012208c360945c22c858ceadbbd48c6e6c294406df7924dff9f4699711f9e68438e99"}{:hash "e301017012206a6123454f2c2a60cc99bd35f1b2f23ce53b463747567942242b0fc8034a9aae"}{:hash "e301017012203edd5d845830143654ff157d255e7e192b38e7385a168994af5150369846def3"}{:hash "e30101701220f4deca7b131d6519cd1b84cb68af67ae2fd124e997109e202767013898fd41f0"}{:hash "e301017012209514fe9fed40e5356ec27fde37a83b211be252ca1c007826c509b36a2f35b9d9"}{:hash "e30101701220e38e9388949745d2dc7b5cab674490cd6995c1c8f49a89426a9e59b449a40e08"}{:hash "e301017012202201b44bb6435581cbd19ee5100cfa6fa947502e48564d1778e11454a26c8a35"}{:hash "e301017012208fa9755e354fe7e9f7790a7eca51bcdf111ae019b3273893cfa6f6773f954cca"}{:hash "e3010170122076a91f4273040de42e45ab622a52d5721be6b24e501c87af1b82fec712f40248"}{:hash "e30101701220911b38061a8eb46b60a50feba033b81c4707cfd3a8b062778dd25521d108a132"}{:hash "e30101701220050dfacc443391966c7de52d8e329823eab5653e48828945d7a17d727e6291bf"}]}}
|
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 37 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "ETHDenver Bufficorn"
|
||||
:author "ETHDenver"
|
||||
:thumbnail "e30101701220d06f13f3de8da081ef2a1bc36ffa283c1bfe093bf45bc0332a6d748196e8ce16"
|
||||
:preview "e30101701220a62487ef23b1bbdc2bf39583bb4259bda032450ac90d199eec8b0b74fe8de580"
|
||||
:stickers [{:hash "e301017012209cba61faaa78931ddee49461ab1301a88a034f077ced9de6351126470b80fe32"}{:hash "e30101701220e8109e8f4bf398a252bfb8d88fe44554c502c5ab8025e7e1abba692028333a97"}{:hash "e30101701220079952b304013fe6bcd5ac00a7480e467badece24a19b00ddea64635d9d5ecae"}{:hash "e301017012200a21dc7c6ef96cdb3f9b53ed1fdea4ee22baa8264a208c3705a8836b664efd8d"}{:hash "e30101701220b3ab61589851545f2c3ce031beafbf808070c36e6abbaf8c7c8076458c3f06e0"}{:hash "e30101701220a5274e0415793e1a81347f594715d6006538d78819a731324d3fec19ab762deb"}{:hash "e3010170122076ae7e71383586ecfa0a6f9cc8af80e614ae466facabe48bee167d2921dd0420"}{:hash "e30101701220bfe0dd7e214eac7cb75c7e8f6e9b1d02732e01ffc67a460cf3b1311aed92a2e9"}{:hash "e3010170122042aa1a5e8dc25072ee6d0b43535701fcb44c58b18e6c14a4956a3212f64cf236"}{:hash "e30101701220107d2ca1901cd8ac0ce54cbf93f5ac86d931c5d4bb16d402315a2a8197dac371"}{:hash "e301017012206d2d66f7f2fff9f366e910f4d157f7ac59dead3e42b2f9fbe5a9b95aea146d10"}{:hash "e301017012203a86612e82ea0db8248875e7993e73a65ee263f6bbeb2b1738eb12a6ec572965"}{:hash "e301017012205bc05fe6517cc00e95e34ba978dd2a5cee91e4dc4efceb4505fecc38887bb7fb"}{:hash "e301017012203cea2a96032284de80587e994c669a42405d49ce599281866ccc14f475e7870b"}]}}
|
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Shiba"
|
||||
:author "Mugen Feng"
|
||||
:thumbnail "e3010170122053ca0e6594e5e11a0b28b2ede71c26409f996fad419010c20d075f4452463300"
|
||||
:preview "e3010170122089927199d0149f15a62f3005e531c2cc1a71f145c782a72e393fe4e3a1bbe23f"
|
||||
:stickers [{:hash "e30101701220cb70a410e0921d68c9fe2f7916a599fe4699f01b8a1b303c33d153117bde12dd"}{:hash "e30101701220c236c51526c0c7d8f53545a35ded32af9f70f78966abf8b4b8e378e4093418a9"}{:hash "e301017012202ae8876381cdae1f165fefef802d8dd9bb32b5e981e90398a01b4620e9261b57"}{:hash "e30101701220b5b71aedbb89fe32bd0090545ebe5c8665c9e153883b57bbd34411863831b3d6"}{:hash "e301017012205f371987eb222cc08918f45ccf94452de79af6bae57bbe027253090f8a849b66"}{:hash "e30101701220c6c45a2058a34b0a3e2728da3b4a68c06fb91de11e511fb09934879318816390"}{:hash "e30101701220ec871a23a328d72c1da2f8e2bfff18f956ca054524c1b97476e0cf27239d31fd"}{:hash "e30101701220b4300cccb984e893e5359f1c426a0d2c4c00407a50475dad9046818726a74168"}{:hash "e301017012207c7c90b29e1200435f6753e3e9f62b5f18198403c060ae4448b25151a0772a54"}{:hash "e3010170122044d99cfab8a61d3f3f6fa111edc7fa81cfd3a6931e2b104c5e3194d4500f7867"}{:hash "e301017012205731049b7d31524573e83b07ebdc262278d876737623a60dba3557d9bdb90867"}{:hash "e301017012209154ef3dd24e3a43167388c1b2f287b93d32f9cce0c87e07348e7308e5512873"}]}}
|
After Width: | Height: | Size: 6.2 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Stanny"
|
||||
:author "DesignBlock"
|
||||
:thumbnail "e30101701220602163b4f56c747333f43775fdcbe4e62d6a3e147b22aaf6097ce0143a6b2373"
|
||||
:preview "e30101701220ef54a5354b78ef82e542bd468f58804de71c8ec268da7968a1422909357f2456"
|
||||
:stickers [{:hash "e301017012207737b75367b8068e5bdd027d7b71a25138c83e155d1f0c9bc5c48ff158724495"}{:hash "e301017012201a9cdea03f27cda1aede7315f79579e160c7b2b6a2eb51a66e47a96f47fe5284"}{:hash "e301017012203d9f7e1a537d4cbec0a4b6b97c542443de1b4c2bd8cd72fcf7889e32581e65c6"}{:hash "e30101701220bef02caced8a4df22030b1781983d19626af66d9f22a951411257bd1cd3f0b33"}{:hash "e3010170122056d1eabeddcbc6e096ce9ff8166dbf0b9b9cdd07e97192b604feb89a22f2a9c4"}{:hash "e30101701220a8cb76e481f3633cf41191f39e8d377a33ec73208d954085f5f3d401054dd351"}{:hash "e301017012205c4fa130b5be0286cbace972bcaf6c6546e22e33be4452e9d36458ed73bcd2c9"}{:hash "e3010170122033c8afe6b6d3f383b8ec77413c31572c6c60f1d788789c9080f180c8aed326a1"}{:hash "e30101701220449bb60c5212998907efa31a93ecbcc0b7870e510735d52f583a9081bbcaf111"}{:hash "e301017012206aa473f11959bf74ca254a0513a008f2f8cb392544bc8a607f7703d83ec43b81"}{:hash "e30101701220c34460dd958fa7e3ec5ba8a8328e49c49f1b44925caedcdb223b709c489f2764"}{:hash "e3010170122088ae75184d06df2768b3e828fe7740ba87267669a4b3c1b1e87b14948347dffe"}{:hash "e30101701220932b074a2e176869827206f9b7c5426f2908c8a306466b71cbdf02ae45ab4673"}{:hash "e301017012207b5a19c6883c33bd34711255989a8ce65ef273e7410d733b9a15859a8a78dcb9"}{:hash "e301017012203af82445123a4d807bd89254e9e4c85839bc5a445b261d61025fbadcc8f0a2f1"}{:hash "e30101701220c958788be30288a7614662a0d082a27400bb0df67308e8b734bfd9cc2d717c53"}{:hash "e30101701220ea5c0b6b611e643a1a3b17dee91c3c2d2840b7fb22a88c69bb0d756fab3d12ff"}{:hash "e30101701220da03e38c2311bf91a15f34287962f05d3fa887f0e0933560190fd6772df67cc3"}{:hash "e301017012204ead438798d856bbe63230a643f97590765ce2fe33f2490564dbaa3760d7a3ab"}{:hash "e3010170122047072b5a1440bc78d78acc5327532d009defdd2eacb4224998b28e3af1f672bf"}{:hash "e301017012200f87d517f8a061a1e276bbc26cd2dfc49ccf481b0f610cb774115ad4eaf4ce99"}{:hash "e3010170122086d32c547734610a3501fcfd574adfa6ec21e5ae25059d26d5431cfaa8b17127"}{:hash "e30101701220f26159a22a9d4cce610ee594d34f7a67c769aa18d2bfb600f94634dbe9fca917"}{:hash "e30101701220c9dad86937cd7d7bb7acc865d6daeb1d341547a761c504a9328104026d1b37e5"}]}}
|
After Width: | Height: | Size: 4.4 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Birdies"
|
||||
:author "olexx"
|
||||
:thumbnail "e3010170122022ff7fbe598b50b9de52831f1f30a291e8a9a5b1ae549c21a47a0c5a4a76c8c1"
|
||||
:preview "e301017012200df5d96c0792c162e9d9a368e315fb9a8718cf049defd93c5380e336fffd186d"
|
||||
:stickers [{:hash "e3010170122096c5525b4251ae22abbc179282160e59d30b7843f29122a5b5f54c4edc0687f7"}{:hash "e301017012200fe2dbc8c2415aa8d13336987c580cb75bf173595136cda4b93c408a8bc698cb"}{:hash "e30101701220377eebcf8641b2bb715a27225fa70b75178f4a4db48b5bc160addd6cf2fd1c3c"}{:hash "e30101701220e3291484446842a665d1a3af4c50f66b0f434ea8d3d2690db41d6da2d9b3df57"}{:hash "e301017012201251f9963e14144183c81b05110f27a09492f08a52eb388e52dc815316982350"}{:hash "e30101701220f8435b33739dee56fec23ba5ec5b527d1d9a4e912742e00bb5773f090a3442e9"}{:hash "e30101701220a1b0e208e696afa0ccc3cfc66876e9c43706a0ae93daa12c4ab02c15c0a903c9"}{:hash "e3010170122059224ad38c272ae798b6c014da52a7d07e500ab980c1357591ff9c983f6a174e"}{:hash "e30101701220db692aadaa897f4027dae1914d6803b2a56d059d706efd967babd1bd5111804e"}{:hash "e30101701220205bc114b2358c5ca01f9e2e6122ab3eb92b1476b3f282ee2a274845a1ab4f43"}{:hash "e3010170122002260af10b74f767bbc5adfdbd5f83c8b0d7782aacef7321cd56fb8c4eb272ea"}{:hash "e30101701220fa37a013fc87760f23c8af91cc3150d4a1f8b57e7efb728149777a1a9237c2db"}{:hash "e30101701220ce694e2a6231127fa2442254a2d94f74d67b7fb594597707dd8861220d2a1313"}{:hash "e301017012203d22f03460e36419f23d8cd74ee09c7d0f5953ba66815605478030ba3ea0c629"}{:hash "e3010170122082a30dbc646dcfb5e447975e50d8c6f396420013e2df9ffe7d89c365cff1beac"}{:hash "e30101701220eea1c3d948182890e3a7528799199389dc2e49d944bd210f0c8a16848f4ceb39"}{:hash "e30101701220b398e1031b8f16ef7aa354b6775611c574b9ede053f98fbcac19f9bfebf0b7c9"}{:hash "e301017012203ea52166c85d25460a1d45b7ba2fcbddad7087af28137278caf1d0c4864c6b27"}{:hash "e301017012204e57cb55a9d6ce467fcbcc88d3ff156ea87f8213cbf8fb120347cf62eb563e2a"}{:hash "e30101701220e1cd4b47bfcca6032e4ca8a2aad030619e2328b8c56bc5d95017953947180c9c"}{:hash "e30101701220ef29467e5890172c26516299c62aaa767b70b2b1b17c9e7c53672942553367cc"}]}}
|
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 6.6 KiB |
|
@ -0,0 +1,5 @@
|
|||
{meta {:name "Ghostatus"
|
||||
:author "Brooklyn Design Factory"
|
||||
:thumbnail "e30101701220a7beb4be086ad31ae19c64e5a832853571e239d9799a923a03779c4435c6fdad"
|
||||
:preview "e3010170122027c67c9acbe98786f6db4aabca3fd3ec04993eaa3e08811aefe27d9786c3bf00"
|
||||
:stickers [{:hash "e30101701220fff8527a1b37070d46c9077877b7f7cc74da5c31adafe77ba65e5efefebf5d91"}{:hash "e301017012208023d8c6bd327b0ac2be66423d59776a753d5f5492975fe0bd5b5601d7c1d9d3"}{:hash "e3010170122064f4e8fa00a5b8164689e038a4d74e0b12f4490dcd4112e80057c254f6fbc135"}{:hash "e301017012200d50bd618b0aed0562ed153de0bf77da766646e81a848982a2f8aaf7d7e94dcc"}{:hash "e3010170122055f08854a40acaac60355d9bb3eaa730b994e2e13484e67d2675103e0cda0c88"}{:hash "e301017012203fc2acfed328918bf000ee637ab4c25fa38f2c69b378b69b9212d61747d30c02"}{:hash "e3010170122096930b99e08c6c28c88c0b74bae7a0159f5c6438ab7d50294987533dabfee863"}{:hash "e3010170122051ddbe29bee4bbc5fcf50d81faad0872f32b88cea4e4e4fcdbf2daf5d09eda76"}{:hash "e301017012200647e07651c163515ce34d18b3c8636eeb4798dbaa1766b2a60facc59999b261"}{:hash "e30101701220c539bfa744e39cf2ece1ab379a15c95338d513a9ce5178d4ad28be486b801bc2"}{:hash "e301017012205ea333b9eb89918ed592f43372bd58dc3a91a7a71aa68b37369c2f66f931fd87"}{:hash "e3010170122007f05ba31bd77003bff562ed932a8b440de1ad05481dc622b1c0c571d6b39ffc"}{:hash "e30101701220906b7a664a87707db72921cf5c7416c61a717dfcb5fcff9bc04b28c612ae554d"}]}}
|