yeriasdk 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- yeriasdk-1.0.0/PKG-INFO +141 -0
- yeriasdk-1.0.0/README.md +114 -0
- yeriasdk-1.0.0/pyproject.toml +61 -0
- yeriasdk-1.0.0/setup.cfg +4 -0
- yeriasdk-1.0.0/tests/test_form_view.py +149 -0
- yeriasdk-1.0.0/tests/test_intro_fields.py +70 -0
- yeriasdk-1.0.0/tests/test_jsonapp.py +107 -0
- yeriasdk-1.0.0/tests/test_validators.py +71 -0
- yeriasdk-1.0.0/yeriasdk/__init__.py +108 -0
- yeriasdk-1.0.0/yeriasdk/core/__init__.py +25 -0
- yeriasdk-1.0.0/yeriasdk/core/base_view.py +520 -0
- yeriasdk-1.0.0/yeriasdk/core/jsonapp.py +744 -0
- yeriasdk-1.0.0/yeriasdk/core/key_store.py +134 -0
- yeriasdk-1.0.0/yeriasdk/core/notification.py +29 -0
- yeriasdk-1.0.0/yeriasdk/errors/__init__.py +76 -0
- yeriasdk-1.0.0/yeriasdk/errors/exceptions.py +493 -0
- yeriasdk-1.0.0/yeriasdk/types/__init__.py +128 -0
- yeriasdk-1.0.0/yeriasdk/types/models.py +606 -0
- yeriasdk-1.0.0/yeriasdk/utils/__init__.py +14 -0
- yeriasdk-1.0.0/yeriasdk/utils/file_formats.py +202 -0
- yeriasdk-1.0.0/yeriasdk/utils/validators.py +751 -0
- yeriasdk-1.0.0/yeriasdk/views/__init__.py +32 -0
- yeriasdk-1.0.0/yeriasdk/views/action_grid_view.py +115 -0
- yeriasdk-1.0.0/yeriasdk/views/action_list_view.py +88 -0
- yeriasdk-1.0.0/yeriasdk/views/base_action_view.py +163 -0
- yeriasdk-1.0.0/yeriasdk/views/card_view.py +143 -0
- yeriasdk-1.0.0/yeriasdk/views/carousel_view.py +189 -0
- yeriasdk-1.0.0/yeriasdk/views/form_view.py +474 -0
- yeriasdk-1.0.0/yeriasdk/views/map_view.py +505 -0
- yeriasdk-1.0.0/yeriasdk/views/media_view.py +112 -0
- yeriasdk-1.0.0/yeriasdk/views/message_view.py +106 -0
- yeriasdk-1.0.0/yeriasdk/views/qr_display_view.py +88 -0
- yeriasdk-1.0.0/yeriasdk/views/qr_scan_view.py +157 -0
- yeriasdk-1.0.0/yeriasdk/views/reader_view.py +270 -0
- yeriasdk-1.0.0/yeriasdk/views/timeline_view.py +108 -0
- yeriasdk-1.0.0/yeriasdk.egg-info/PKG-INFO +141 -0
- yeriasdk-1.0.0/yeriasdk.egg-info/SOURCES.txt +38 -0
- yeriasdk-1.0.0/yeriasdk.egg-info/dependency_links.txt +1 -0
- yeriasdk-1.0.0/yeriasdk.egg-info/requires.txt +11 -0
- yeriasdk-1.0.0/yeriasdk.egg-info/top_level.txt +2 -0
yeriasdk-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yeriasdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A modern Python SDK for generating dynamic JSON interfaces
|
|
5
|
+
Author: JsonApp Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: json,interface,form-builder,sdk,dynamic-ui,mobile-apps
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: cryptography>=41.0.0
|
|
18
|
+
Requires-Dist: markdown>=3.4.0
|
|
19
|
+
Requires-Dist: bleach>=6.0.0
|
|
20
|
+
Requires-Dist: requests>=2.28.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
24
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
25
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff>=0.0.280; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# JSONApp Python SDK
|
|
29
|
+
|
|
30
|
+
> **Note**: This is the Python port of the JSONApp SDK. This repository is a monorepo containing multiple language implementations. See the [root README](../README.md) for an overview.
|
|
31
|
+
|
|
32
|
+
A stateless backend library for building views that are sent to renderers (mobile or web). This is the Python port of the TypeScript JSONApp SDK.
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- **Stateless Architecture**: No internal state, perfect for serverless and microservices
|
|
37
|
+
- **Ed25519 Signing**: Secure view signing and verification using Ed25519 cryptography
|
|
38
|
+
- **12 View Types**: Form, Reader, ActionList, ActionGrid, QRScan, QRDisplay, Message, Card, Carousel, Timeline, Media, Map
|
|
39
|
+
- **Notifications**: Send signed notifications to users via City-Mate platform
|
|
40
|
+
- **Type Safety**: Full type definitions using Python dataclasses and type hints
|
|
41
|
+
- **Validation**: Built-in field and form validation
|
|
42
|
+
- **Security**: XSS protection, URL validation, input sanitization
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install yeriasdk
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from yeriasdk import JsonApp, JsonAppConfig
|
|
54
|
+
from yeriasdk.views import FormView
|
|
55
|
+
|
|
56
|
+
# Initialize JsonApp
|
|
57
|
+
config = JsonAppConfig(
|
|
58
|
+
app_id="my-app",
|
|
59
|
+
view_expiration_minutes=60,
|
|
60
|
+
)
|
|
61
|
+
app = JsonApp(config)
|
|
62
|
+
|
|
63
|
+
# Create a form view
|
|
64
|
+
form = app.create_form_view("registration", "User Registration")
|
|
65
|
+
form.add_text_field("name", "Full Name", is_required=True)
|
|
66
|
+
form.add_email_field("email", "Email", is_required=True)
|
|
67
|
+
form.submit_button("Register")
|
|
68
|
+
|
|
69
|
+
# Serve with signature
|
|
70
|
+
response = app.serve(form)
|
|
71
|
+
print(response.view) # The view JSON
|
|
72
|
+
print(response.signature) # Ed25519 signature
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## View Types
|
|
76
|
+
|
|
77
|
+
### FormView
|
|
78
|
+
Create forms with various field types (text, email, password, select, file, GPS, etc.)
|
|
79
|
+
|
|
80
|
+
### ReaderView
|
|
81
|
+
Display rich content with paragraphs, images, markdown, tables, code blocks, etc.
|
|
82
|
+
|
|
83
|
+
### ActionListView / ActionGridView
|
|
84
|
+
Display lists or grids of actions
|
|
85
|
+
|
|
86
|
+
### QRScanView / QRDisplayView
|
|
87
|
+
QR code scanning and display
|
|
88
|
+
|
|
89
|
+
### MessageView
|
|
90
|
+
Display messages with actions
|
|
91
|
+
|
|
92
|
+
### CardView
|
|
93
|
+
Display card-based content with stats and sections
|
|
94
|
+
|
|
95
|
+
### CarouselView
|
|
96
|
+
Display carousel slides
|
|
97
|
+
|
|
98
|
+
### TimelineView
|
|
99
|
+
Display chronological events
|
|
100
|
+
|
|
101
|
+
### MediaView
|
|
102
|
+
Display audio and video playlists
|
|
103
|
+
|
|
104
|
+
### MapView
|
|
105
|
+
Display geographic data on maps
|
|
106
|
+
|
|
107
|
+
## API Parity
|
|
108
|
+
|
|
109
|
+
The Python SDK maintains API parity with the TypeScript version:
|
|
110
|
+
|
|
111
|
+
- Same factory methods: `app.create_form_view()`, `app.create_reader_view()`, etc.
|
|
112
|
+
- Same fluent API: `view.add_field().set_intro().submit_button()` (or `set_note()` for backward compatibility)
|
|
113
|
+
- Same validation and security features
|
|
114
|
+
- Same Ed25519 signing and verification
|
|
115
|
+
|
|
116
|
+
## Examples
|
|
117
|
+
|
|
118
|
+
See `examples/basic_usage.py` for a complete example.
|
|
119
|
+
|
|
120
|
+
## Requirements
|
|
121
|
+
|
|
122
|
+
- Python 3.10+
|
|
123
|
+
- cryptography (for Ed25519)
|
|
124
|
+
- markdown (for ReaderView markdown support)
|
|
125
|
+
- bleach (for HTML sanitization)
|
|
126
|
+
- requests (for sending notifications)
|
|
127
|
+
|
|
128
|
+
## Status
|
|
129
|
+
|
|
130
|
+
✅ Core classes (BaseView, JsonApp)
|
|
131
|
+
✅ All 12 view types
|
|
132
|
+
✅ Type definitions
|
|
133
|
+
✅ Error handling
|
|
134
|
+
✅ Validation utilities
|
|
135
|
+
✅ Ed25519 signing/verification
|
|
136
|
+
✅ Examples
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
Ce projet est sous licence MIT. Voir le fichier `LICENSE` pour plus de détails.
|
yeriasdk-1.0.0/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# JSONApp Python SDK
|
|
2
|
+
|
|
3
|
+
> **Note**: This is the Python port of the JSONApp SDK. This repository is a monorepo containing multiple language implementations. See the [root README](../README.md) for an overview.
|
|
4
|
+
|
|
5
|
+
A stateless backend library for building views that are sent to renderers (mobile or web). This is the Python port of the TypeScript JSONApp SDK.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Stateless Architecture**: No internal state, perfect for serverless and microservices
|
|
10
|
+
- **Ed25519 Signing**: Secure view signing and verification using Ed25519 cryptography
|
|
11
|
+
- **12 View Types**: Form, Reader, ActionList, ActionGrid, QRScan, QRDisplay, Message, Card, Carousel, Timeline, Media, Map
|
|
12
|
+
- **Notifications**: Send signed notifications to users via City-Mate platform
|
|
13
|
+
- **Type Safety**: Full type definitions using Python dataclasses and type hints
|
|
14
|
+
- **Validation**: Built-in field and form validation
|
|
15
|
+
- **Security**: XSS protection, URL validation, input sanitization
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install yeriasdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from yeriasdk import JsonApp, JsonAppConfig
|
|
27
|
+
from yeriasdk.views import FormView
|
|
28
|
+
|
|
29
|
+
# Initialize JsonApp
|
|
30
|
+
config = JsonAppConfig(
|
|
31
|
+
app_id="my-app",
|
|
32
|
+
view_expiration_minutes=60,
|
|
33
|
+
)
|
|
34
|
+
app = JsonApp(config)
|
|
35
|
+
|
|
36
|
+
# Create a form view
|
|
37
|
+
form = app.create_form_view("registration", "User Registration")
|
|
38
|
+
form.add_text_field("name", "Full Name", is_required=True)
|
|
39
|
+
form.add_email_field("email", "Email", is_required=True)
|
|
40
|
+
form.submit_button("Register")
|
|
41
|
+
|
|
42
|
+
# Serve with signature
|
|
43
|
+
response = app.serve(form)
|
|
44
|
+
print(response.view) # The view JSON
|
|
45
|
+
print(response.signature) # Ed25519 signature
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## View Types
|
|
49
|
+
|
|
50
|
+
### FormView
|
|
51
|
+
Create forms with various field types (text, email, password, select, file, GPS, etc.)
|
|
52
|
+
|
|
53
|
+
### ReaderView
|
|
54
|
+
Display rich content with paragraphs, images, markdown, tables, code blocks, etc.
|
|
55
|
+
|
|
56
|
+
### ActionListView / ActionGridView
|
|
57
|
+
Display lists or grids of actions
|
|
58
|
+
|
|
59
|
+
### QRScanView / QRDisplayView
|
|
60
|
+
QR code scanning and display
|
|
61
|
+
|
|
62
|
+
### MessageView
|
|
63
|
+
Display messages with actions
|
|
64
|
+
|
|
65
|
+
### CardView
|
|
66
|
+
Display card-based content with stats and sections
|
|
67
|
+
|
|
68
|
+
### CarouselView
|
|
69
|
+
Display carousel slides
|
|
70
|
+
|
|
71
|
+
### TimelineView
|
|
72
|
+
Display chronological events
|
|
73
|
+
|
|
74
|
+
### MediaView
|
|
75
|
+
Display audio and video playlists
|
|
76
|
+
|
|
77
|
+
### MapView
|
|
78
|
+
Display geographic data on maps
|
|
79
|
+
|
|
80
|
+
## API Parity
|
|
81
|
+
|
|
82
|
+
The Python SDK maintains API parity with the TypeScript version:
|
|
83
|
+
|
|
84
|
+
- Same factory methods: `app.create_form_view()`, `app.create_reader_view()`, etc.
|
|
85
|
+
- Same fluent API: `view.add_field().set_intro().submit_button()` (or `set_note()` for backward compatibility)
|
|
86
|
+
- Same validation and security features
|
|
87
|
+
- Same Ed25519 signing and verification
|
|
88
|
+
|
|
89
|
+
## Examples
|
|
90
|
+
|
|
91
|
+
See `examples/basic_usage.py` for a complete example.
|
|
92
|
+
|
|
93
|
+
## Requirements
|
|
94
|
+
|
|
95
|
+
- Python 3.10+
|
|
96
|
+
- cryptography (for Ed25519)
|
|
97
|
+
- markdown (for ReaderView markdown support)
|
|
98
|
+
- bleach (for HTML sanitization)
|
|
99
|
+
- requests (for sending notifications)
|
|
100
|
+
|
|
101
|
+
## Status
|
|
102
|
+
|
|
103
|
+
✅ Core classes (BaseView, JsonApp)
|
|
104
|
+
✅ All 12 view types
|
|
105
|
+
✅ Type definitions
|
|
106
|
+
✅ Error handling
|
|
107
|
+
✅ Validation utilities
|
|
108
|
+
✅ Ed25519 signing/verification
|
|
109
|
+
✅ Examples
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
Ce projet est sous licence MIT. Voir le fichier `LICENSE` pour plus de détails.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=65.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "yeriasdk"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A modern Python SDK for generating dynamic JSON interfaces"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "JsonApp Team" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["json", "interface", "form-builder", "sdk", "dynamic-ui", "mobile-apps"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
dependencies = [
|
|
27
|
+
"cryptography>=41.0.0",
|
|
28
|
+
"markdown>=3.4.0",
|
|
29
|
+
"bleach>=6.0.0",
|
|
30
|
+
"requests>=2.28.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=7.4.0",
|
|
36
|
+
"pytest-cov>=4.1.0",
|
|
37
|
+
"black>=23.0.0",
|
|
38
|
+
"mypy>=1.5.0",
|
|
39
|
+
"ruff>=0.0.280",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["."]
|
|
44
|
+
exclude = ["tests*", "examples*"]
|
|
45
|
+
|
|
46
|
+
[tool.black]
|
|
47
|
+
line-length = 100
|
|
48
|
+
target-version = ['py310']
|
|
49
|
+
|
|
50
|
+
[tool.mypy]
|
|
51
|
+
python_version = "3.10"
|
|
52
|
+
warn_return_any = true
|
|
53
|
+
warn_unused_configs = true
|
|
54
|
+
disallow_untyped_defs = false
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
python_files = ["test_*.py"]
|
|
59
|
+
python_classes = ["Test*"]
|
|
60
|
+
python_functions = ["test_*"]
|
|
61
|
+
|
yeriasdk-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for FormView - Critical functionality including separators and intro field
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from jsonapp import JsonApp, JsonAppConfig
|
|
7
|
+
from jsonapp.views import FormView
|
|
8
|
+
from jsonapp.errors import (
|
|
9
|
+
MissingRequiredParameterError,
|
|
10
|
+
FieldValidationError,
|
|
11
|
+
FieldNotFoundError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestFormView:
|
|
16
|
+
"""Test FormView basic functionality"""
|
|
17
|
+
|
|
18
|
+
def test_create_form_view(self):
|
|
19
|
+
"""Test creating a basic form view"""
|
|
20
|
+
form = FormView("test-form", "Test Form")
|
|
21
|
+
assert form.id == "test-form"
|
|
22
|
+
assert form.type == "Form"
|
|
23
|
+
assert form.content["title"] == "Test Form"
|
|
24
|
+
assert form.content["intro"] == ""
|
|
25
|
+
assert form.content["fields"] == []
|
|
26
|
+
|
|
27
|
+
def test_set_intro(self):
|
|
28
|
+
"""Test setting intro field"""
|
|
29
|
+
form = FormView("test-form", "Test Form")
|
|
30
|
+
form.set_intro("Please fill in the form")
|
|
31
|
+
assert form.content["intro"] == "Please fill in the form"
|
|
32
|
+
|
|
33
|
+
def test_set_note_deprecated(self):
|
|
34
|
+
"""Test that set_note() still works for backward compatibility"""
|
|
35
|
+
form = FormView("test-form", "Test Form")
|
|
36
|
+
form.set_note("Old note method")
|
|
37
|
+
assert form.content["intro"] == "Old note method"
|
|
38
|
+
|
|
39
|
+
def test_add_text_field(self):
|
|
40
|
+
"""Test adding a text field"""
|
|
41
|
+
form = FormView("test-form", "Test Form")
|
|
42
|
+
form.add_text_field("name", "Full Name", is_required=True)
|
|
43
|
+
assert len(form.content["fields"]) == 1
|
|
44
|
+
field = form.content["fields"][0]
|
|
45
|
+
assert field["fieldType"] == "text"
|
|
46
|
+
assert field["fieldId"] == "name"
|
|
47
|
+
assert field["fieldLabel"] == "Full Name"
|
|
48
|
+
assert field["required"] is True
|
|
49
|
+
|
|
50
|
+
def test_add_separator(self):
|
|
51
|
+
"""Test adding a separator field"""
|
|
52
|
+
form = FormView("test-form", "Test Form")
|
|
53
|
+
form.add_text_field("name", "Name")
|
|
54
|
+
form.add_separator()
|
|
55
|
+
form.add_email_field("email", "Email")
|
|
56
|
+
|
|
57
|
+
assert len(form.content["fields"]) == 3
|
|
58
|
+
separator = form.content["fields"][1]
|
|
59
|
+
assert separator["fieldType"] == "separator"
|
|
60
|
+
assert separator["fieldLabel"] == ""
|
|
61
|
+
assert "fieldId" in separator
|
|
62
|
+
|
|
63
|
+
def test_add_separator_with_id(self):
|
|
64
|
+
"""Test adding a separator with explicit ID"""
|
|
65
|
+
form = FormView("test-form", "Test Form")
|
|
66
|
+
form.add_separator("custom-separator")
|
|
67
|
+
|
|
68
|
+
separator = form.content["fields"][0]
|
|
69
|
+
assert separator["fieldType"] == "separator"
|
|
70
|
+
assert separator["fieldId"] == "custom-separator"
|
|
71
|
+
|
|
72
|
+
def test_get_field_count(self):
|
|
73
|
+
"""Test get_field_count() method"""
|
|
74
|
+
form = FormView("test-form", "Test Form")
|
|
75
|
+
form.add_text_field("name", "Name")
|
|
76
|
+
form.add_separator()
|
|
77
|
+
form.add_email_field("email", "Email")
|
|
78
|
+
|
|
79
|
+
assert form.get_field_count() == 3
|
|
80
|
+
assert form.get_field_count(exclude_separators=True) == 2
|
|
81
|
+
|
|
82
|
+
def test_submit_button(self):
|
|
83
|
+
"""Test setting submit button"""
|
|
84
|
+
form = FormView("test-form", "Test Form")
|
|
85
|
+
form.add_text_field("name", "Name")
|
|
86
|
+
form.submit_button("Submit", "POST")
|
|
87
|
+
|
|
88
|
+
assert form.content["submit"] is not None
|
|
89
|
+
assert form.content["submit"]["text"] == "Submit"
|
|
90
|
+
assert form.content["submit"]["method"] == "POST"
|
|
91
|
+
|
|
92
|
+
def test_validation_requires_field(self):
|
|
93
|
+
"""Test that form validation requires at least one non-separator field"""
|
|
94
|
+
form = FormView("test-form", "Test Form")
|
|
95
|
+
form.add_separator()
|
|
96
|
+
|
|
97
|
+
result = form._validate()
|
|
98
|
+
assert result.is_valid is False
|
|
99
|
+
assert any("non-separator field" in str(e.message) for e in result.errors)
|
|
100
|
+
|
|
101
|
+
def test_validation_with_real_fields(self):
|
|
102
|
+
"""Test that form with real fields passes validation"""
|
|
103
|
+
form = FormView("test-form", "Test Form")
|
|
104
|
+
form.add_text_field("name", "Name")
|
|
105
|
+
form.add_separator()
|
|
106
|
+
form.add_email_field("email", "Email")
|
|
107
|
+
|
|
108
|
+
result = form._validate()
|
|
109
|
+
assert result.is_valid is True
|
|
110
|
+
|
|
111
|
+
def test_get_field(self):
|
|
112
|
+
"""Test getting a field by ID"""
|
|
113
|
+
form = FormView("test-form", "Test Form")
|
|
114
|
+
form.add_text_field("name", "Name")
|
|
115
|
+
|
|
116
|
+
field = form.get_field("name")
|
|
117
|
+
assert field is not None
|
|
118
|
+
assert field["fieldId"] == "name"
|
|
119
|
+
|
|
120
|
+
def test_get_field_not_found(self):
|
|
121
|
+
"""Test getting a non-existent field"""
|
|
122
|
+
form = FormView("test-form", "Test Form")
|
|
123
|
+
field = form.get_field("nonexistent")
|
|
124
|
+
assert field is None
|
|
125
|
+
|
|
126
|
+
def test_remove_field(self):
|
|
127
|
+
"""Test removing a field"""
|
|
128
|
+
form = FormView("test-form", "Test Form")
|
|
129
|
+
form.add_text_field("name", "Name")
|
|
130
|
+
form.add_email_field("email", "Email")
|
|
131
|
+
|
|
132
|
+
result = form.remove_field("name")
|
|
133
|
+
assert result is True
|
|
134
|
+
assert len(form.content["fields"]) == 1
|
|
135
|
+
assert form.content["fields"][0]["fieldId"] == "email"
|
|
136
|
+
|
|
137
|
+
def test_inject_data(self):
|
|
138
|
+
"""Test injecting data into form fields"""
|
|
139
|
+
form = FormView("test-form", "Test Form")
|
|
140
|
+
form.add_text_field("name", "Name")
|
|
141
|
+
form.add_email_field("email", "Email")
|
|
142
|
+
|
|
143
|
+
errors = form.inject_data({"name": "John Doe", "email": "john@example.com"})
|
|
144
|
+
assert len(errors) == 0
|
|
145
|
+
|
|
146
|
+
name_field = form.get_field("name")
|
|
147
|
+
assert name_field["value"] == "John Doe"
|
|
148
|
+
|
|
149
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for intro field support across all views
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from jsonapp import JsonApp, JsonAppConfig
|
|
7
|
+
from jsonapp.views import (
|
|
8
|
+
ActionListView,
|
|
9
|
+
ActionGridView,
|
|
10
|
+
MapView,
|
|
11
|
+
ReaderView,
|
|
12
|
+
QRScanView,
|
|
13
|
+
MessageView,
|
|
14
|
+
TimelineView,
|
|
15
|
+
MediaView,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TestIntroFields:
|
|
20
|
+
"""Test intro field support across views"""
|
|
21
|
+
|
|
22
|
+
def test_action_list_view_intro(self):
|
|
23
|
+
"""Test ActionListView intro support"""
|
|
24
|
+
view = ActionListView("test-list", "Test List")
|
|
25
|
+
view.set_intro("Select an option")
|
|
26
|
+
assert view.content["intro"] == "Select an option"
|
|
27
|
+
|
|
28
|
+
def test_action_grid_view_intro(self):
|
|
29
|
+
"""Test ActionGridView intro support"""
|
|
30
|
+
view = ActionGridView("test-grid", "Test Grid")
|
|
31
|
+
view.set_intro("Choose an action")
|
|
32
|
+
assert view.content["intro"] == "Choose an action"
|
|
33
|
+
|
|
34
|
+
def test_map_view_intro(self):
|
|
35
|
+
"""Test MapView intro support"""
|
|
36
|
+
view = MapView("test-map", "Test Map")
|
|
37
|
+
view.set_intro("Find locations on the map")
|
|
38
|
+
assert view.content["intro"] == "Find locations on the map"
|
|
39
|
+
|
|
40
|
+
def test_reader_view_intro(self):
|
|
41
|
+
"""Test ReaderView intro support"""
|
|
42
|
+
view = ReaderView("test-reader", "Test Reader")
|
|
43
|
+
view.set_intro("Read the content below")
|
|
44
|
+
assert view.content["intro"] == "Read the content below"
|
|
45
|
+
|
|
46
|
+
def test_qr_scan_view_intro(self):
|
|
47
|
+
"""Test QRScanView intro support"""
|
|
48
|
+
view = QRScanView("test-scan", "Test Scanner")
|
|
49
|
+
view.set_intro("Point camera at QR code")
|
|
50
|
+
assert view.content["intro"] == "Point camera at QR code"
|
|
51
|
+
|
|
52
|
+
def test_message_view_intro(self):
|
|
53
|
+
"""Test MessageView intro support"""
|
|
54
|
+
view = MessageView("test-msg", "Test Message")
|
|
55
|
+
view.set_intro("Welcome message")
|
|
56
|
+
assert view.content["intro"] == "Welcome message"
|
|
57
|
+
|
|
58
|
+
def test_timeline_view_intro(self):
|
|
59
|
+
"""Test TimelineView intro support"""
|
|
60
|
+
view = TimelineView("test-timeline", "Test Timeline")
|
|
61
|
+
view.set_intro("View your timeline")
|
|
62
|
+
assert view.content["intro"] == "View your timeline"
|
|
63
|
+
|
|
64
|
+
def test_media_view_intro(self):
|
|
65
|
+
"""Test MediaView intro support"""
|
|
66
|
+
view = MediaView("test-media", "Test Media")
|
|
67
|
+
view.set_intro("Browse media")
|
|
68
|
+
assert view.content["intro"] == "Browse media"
|
|
69
|
+
|
|
70
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for JsonApp - Signing, verification, and factory methods
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
import time
|
|
7
|
+
from jsonapp import JsonApp, JsonAppConfig
|
|
8
|
+
from jsonapp.views import FormView
|
|
9
|
+
from jsonapp.errors import (
|
|
10
|
+
AppIdMismatchError,
|
|
11
|
+
ViewExpiredError,
|
|
12
|
+
SignatureVerificationError,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestJsonApp:
|
|
17
|
+
"""Test JsonApp core functionality"""
|
|
18
|
+
|
|
19
|
+
def test_create_jsonapp(self):
|
|
20
|
+
"""Test creating JsonApp instance"""
|
|
21
|
+
config = JsonAppConfig(app_id="test-app", view_expiration_minutes=60)
|
|
22
|
+
app = JsonApp(config)
|
|
23
|
+
assert app.config.app_id == "test-app"
|
|
24
|
+
assert app.config.view_expiration_minutes == 60
|
|
25
|
+
|
|
26
|
+
def test_create_form_view(self):
|
|
27
|
+
"""Test factory method for FormView"""
|
|
28
|
+
config = JsonAppConfig(app_id="test-app")
|
|
29
|
+
app = JsonApp(config)
|
|
30
|
+
form = app.create_form_view("test-form", "Test Form")
|
|
31
|
+
assert isinstance(form, FormView)
|
|
32
|
+
assert form.id == "test-form"
|
|
33
|
+
|
|
34
|
+
def test_serve_view(self):
|
|
35
|
+
"""Test serving a view with signature"""
|
|
36
|
+
config = JsonAppConfig(app_id="test-app")
|
|
37
|
+
app = JsonApp(config)
|
|
38
|
+
form = app.create_form_view("test-form", "Test Form")
|
|
39
|
+
form.add_text_field("name", "Name")
|
|
40
|
+
|
|
41
|
+
response = app.serve(form)
|
|
42
|
+
assert response.app_id == "test-app"
|
|
43
|
+
assert response.signature is not None
|
|
44
|
+
assert response.timestamp > 0
|
|
45
|
+
assert response.view is not None
|
|
46
|
+
assert response.view["id"] == "test-form"
|
|
47
|
+
|
|
48
|
+
def test_get_public_key(self):
|
|
49
|
+
"""Test getting public key"""
|
|
50
|
+
config = JsonAppConfig(app_id="test-app")
|
|
51
|
+
app = JsonApp(config)
|
|
52
|
+
public_key = app.get_public_key()
|
|
53
|
+
assert public_key is not None
|
|
54
|
+
assert "BEGIN PUBLIC KEY" in public_key or "BEGIN PUBLIC KEY" in public_key
|
|
55
|
+
|
|
56
|
+
def test_verify_integrity_success(self):
|
|
57
|
+
"""Test verifying integrity of a valid response"""
|
|
58
|
+
config = JsonAppConfig(app_id="test-app", view_expiration_minutes=60)
|
|
59
|
+
app = JsonApp(config)
|
|
60
|
+
form = app.create_form_view("test-form", "Test Form")
|
|
61
|
+
form.add_text_field("name", "Name")
|
|
62
|
+
|
|
63
|
+
response = app.serve(form)
|
|
64
|
+
result = app.verify_integrity(response)
|
|
65
|
+
assert result is True
|
|
66
|
+
|
|
67
|
+
def test_verify_integrity_app_id_mismatch(self):
|
|
68
|
+
"""Test verification fails with wrong app ID"""
|
|
69
|
+
config1 = JsonAppConfig(app_id="app-1")
|
|
70
|
+
app1 = JsonApp(config1)
|
|
71
|
+
form = app1.create_form_view("test-form", "Test Form")
|
|
72
|
+
form.add_text_field("name", "Name")
|
|
73
|
+
response = app1.serve(form)
|
|
74
|
+
|
|
75
|
+
config2 = JsonAppConfig(app_id="app-2")
|
|
76
|
+
app2 = JsonApp(config2)
|
|
77
|
+
|
|
78
|
+
with pytest.raises(AppIdMismatchError):
|
|
79
|
+
app2.verify_integrity(response)
|
|
80
|
+
|
|
81
|
+
def test_static_sign_view(self):
|
|
82
|
+
"""Test static sign_view method"""
|
|
83
|
+
config = JsonAppConfig(app_id="test-app")
|
|
84
|
+
app = JsonApp(config)
|
|
85
|
+
form = app.create_form_view("test-form", "Test Form")
|
|
86
|
+
form.add_text_field("name", "Name")
|
|
87
|
+
view_json = form.to_json()
|
|
88
|
+
|
|
89
|
+
private_key = app.get_public_key() # In real usage, use private key
|
|
90
|
+
# Note: This test would need actual private key to work properly
|
|
91
|
+
# For now, just verify the method exists and accepts parameters
|
|
92
|
+
assert hasattr(JsonApp, "sign_view")
|
|
93
|
+
|
|
94
|
+
def test_static_verify_signature(self):
|
|
95
|
+
"""Test static verify_signature method"""
|
|
96
|
+
config = JsonAppConfig(app_id="test-app")
|
|
97
|
+
app = JsonApp(config)
|
|
98
|
+
form = app.create_form_view("test-form", "Test Form")
|
|
99
|
+
form.add_text_field("name", "Name")
|
|
100
|
+
|
|
101
|
+
response = app.serve(form)
|
|
102
|
+
public_key = app.get_public_key()
|
|
103
|
+
|
|
104
|
+
result = JsonApp.verify_signature(public_key, response)
|
|
105
|
+
assert result is True
|
|
106
|
+
|
|
107
|
+
|