@descope-ui/descope-ponyhot 2.2.37

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
+
5
+ ## [2.2.37](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.36...web-components-ui-2.2.37) (2026-01-28)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * Rename honeypot folder to resolve registration mismatch ([#868](https://github.com/descope/web-components-ui/issues/868)) ([7166c17](https://github.com/descope/web-components-ui/commit/7166c1799fa79bf1b1504eb93ca48fd8a486bf53))
11
+
12
+ ## [2.2.36](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.35...web-components-ui-2.2.36) (2026-01-28)
13
+
14
+ ## [2.2.35](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.34...web-components-ui-2.2.35) (2026-01-22)
15
+
16
+ ## [2.2.34](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.33...web-components-ui-2.2.34) (2026-01-21)
17
+
18
+ ## [2.2.33](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.32...web-components-ui-2.2.33) (2026-01-21)
19
+
20
+ ## [2.2.32](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.31...web-components-ui-2.2.32) (2026-01-21)
21
+
22
+ ## [2.2.31](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.30...web-components-ui-2.2.31) (2026-01-21)
23
+
24
+ ## [2.2.30](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.29...web-components-ui-2.2.30) (2026-01-19)
25
+
26
+ ## [2.2.29](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.28...web-components-ui-2.2.29) (2026-01-19)
27
+
28
+ ## [2.2.28](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.27...web-components-ui-2.2.28) (2026-01-19)
29
+
30
+ ## [2.2.27](https://github.com/descope/web-components-ui/compare/web-components-ui-2.2.26...web-components-ui-2.2.27) (2026-01-18)
31
+
32
+ # Changelog
@@ -0,0 +1,174 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { getStoryUrl } from 'e2e-utils';
3
+ import { createHoneypotTestDriver } from 'test-drivers';
4
+
5
+ const storyName = 'descope-honeypot';
6
+ // NOTICE: This component registers with a DIFFERENT name than its file name
7
+ const componentName = 'descope-ponyhot';
8
+
9
+ test.describe('logic', () => {
10
+ test('render honeypot input', async ({ page }) => {
11
+ await page.goto(
12
+ getStoryUrl(storyName, {
13
+ label: 'Enter address',
14
+ type: 'address',
15
+ name: 'dhp-address',
16
+ }),
17
+ {
18
+ waitUntil: 'networkidle',
19
+ },
20
+ );
21
+
22
+ const component = createHoneypotTestDriver(page.locator(componentName));
23
+
24
+ expect(await component.input.getAttribute('name')).toMatch('dhp-address');
25
+ });
26
+
27
+ [
28
+ 'input',
29
+ 'change',
30
+ 'click',
31
+ 'keydown',
32
+ 'keyup',
33
+ 'mouseover',
34
+ 'mousedown',
35
+ ].forEach((eventType) => {
36
+ ['text', 'email', 'address', 'password'].forEach((inputType) => {
37
+ test(`fill honeypot of type "${inputType}" when event "${eventType}" dispatched`, async ({
38
+ page,
39
+ }) => {
40
+ await page.goto(
41
+ getStoryUrl(storyName, {
42
+ label: 'Enter value',
43
+ type: inputType,
44
+ name: `dhp-${inputType}`,
45
+ }),
46
+ {
47
+ waitUntil: 'networkidle',
48
+ },
49
+ );
50
+
51
+ const component = createHoneypotTestDriver(page.locator(componentName));
52
+ await component.input.dispatchEvent(eventType);
53
+
54
+ expect(await component.getInputValue()).toBe(`dhp-${eventType}`);
55
+ });
56
+ });
57
+ });
58
+
59
+ test('should apply hiding styles to component', async ({ page }) => {
60
+ await page.goto(
61
+ getStoryUrl(storyName, {
62
+ label: 'Enter address',
63
+ type: 'address',
64
+ name: 'dhp-address',
65
+ }),
66
+ {
67
+ waitUntil: 'networkidle',
68
+ },
69
+ );
70
+
71
+ const component = createHoneypotTestDriver(page.locator(componentName));
72
+
73
+ const expectedStyles = {
74
+ position: 'absolute',
75
+ opacity: '0.01',
76
+ width: '1px',
77
+ height: '1px',
78
+ 'pointer-events': 'none',
79
+ 'z-index': '-1',
80
+ left: '-999999px',
81
+ };
82
+
83
+ const computedStyle = await component.getHideStyles();
84
+
85
+ Object.entries(expectedStyles).forEach(([styleName, expectedValue]) => {
86
+ expect(computedStyle[styleName]).toBe(expectedValue);
87
+ });
88
+
89
+ expect(await component.getInputInlineStyle()).toBeNull();
90
+ });
91
+
92
+ test('should only trigger once, then lock state', async ({ page }) => {
93
+ await page.goto(
94
+ getStoryUrl(storyName, {
95
+ label: 'Enter address',
96
+ type: 'address',
97
+ name: 'dhp-address',
98
+ }),
99
+ {
100
+ waitUntil: 'networkidle',
101
+ },
102
+ );
103
+
104
+ const component = createHoneypotTestDriver(page.locator(componentName));
105
+
106
+ component.input.dispatchEvent('input');
107
+ expect(await component.getInputValue()).toBe('dhp-input');
108
+
109
+ component.input.dispatchEvent('click');
110
+ expect(await component.getInputValue()).toBe('dhp-input');
111
+
112
+ component.input.dispatchEvent('keyup');
113
+ expect(await component.getInputValue()).toBe('dhp-input');
114
+ });
115
+
116
+ test('should intercept direct value assignment without events', async ({
117
+ page,
118
+ }) => {
119
+ await page.goto(
120
+ getStoryUrl(storyName, {
121
+ label: 'Enter address',
122
+ type: 'address',
123
+ name: 'dhp-address',
124
+ }),
125
+ {
126
+ waitUntil: 'networkidle',
127
+ },
128
+ );
129
+
130
+ const component = createHoneypotTestDriver(page.locator(componentName));
131
+ await component.setInputValue('bot-spam-value');
132
+
133
+ expect(await component.getInputValue()).toBe('dhp-direct-set');
134
+ });
135
+
136
+ test('should allow setting dhp- prefixed values through interceptor', async ({
137
+ page,
138
+ }) => {
139
+ await page.goto(
140
+ getStoryUrl(storyName, {
141
+ label: 'Enter address',
142
+ type: 'address',
143
+ name: 'dhp-address',
144
+ }),
145
+ {
146
+ waitUntil: 'networkidle',
147
+ },
148
+ );
149
+
150
+ const component = createHoneypotTestDriver(page.locator(componentName));
151
+ await component.setInputValue('dhp-click');
152
+
153
+ expect(await component.getInputValue()).toBe('dhp-click');
154
+ });
155
+
156
+ test('should not intercept empty value assignments', async ({ page }) => {
157
+ await page.goto(
158
+ getStoryUrl(storyName, {
159
+ label: 'Enter address',
160
+ type: 'address',
161
+ name: 'dhp-address',
162
+ }),
163
+ {
164
+ waitUntil: 'networkidle',
165
+ },
166
+ );
167
+
168
+ const component = createHoneypotTestDriver(page.locator(componentName));
169
+
170
+ await component.setInputValue('');
171
+
172
+ expect(await component.getInputValue()).toBe('');
173
+ });
174
+ });
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@descope-ui/descope-ponyhot",
3
+ "version": "2.2.37",
4
+ "exports": {
5
+ ".": {
6
+ "import": "./src/component/index.js"
7
+ },
8
+ "./theme": {
9
+ "import": "./src/theme.js"
10
+ },
11
+ "./class": {
12
+ "import": "./src/component/HoneypotClass.js"
13
+ }
14
+ },
15
+ "devDependencies": {
16
+ "@playwright/test": "1.38.1",
17
+ "e2e-utils": "2.2.37",
18
+ "test-assets": "2.2.37",
19
+ "test-drivers": "2.2.37"
20
+ },
21
+ "dependencies": {
22
+ "@descope-ui/common": "2.2.37",
23
+ "@descope-ui/theme-globals": "2.2.37"
24
+ },
25
+ "publishConfig": {
26
+ "link-workspace-packages": false
27
+ },
28
+ "scripts": {
29
+ "test": "echo 'No tests defined' && exit 0",
30
+ "test:e2e": "echo 'No e2e tests defined' && exit 0"
31
+ }
32
+ }
package/project.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@descope-ui/descope-ponyhot",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "packages/web-components/components/descope-ponyhot/src",
5
+ "projectType": "library",
6
+ "targets": {},
7
+ "tags": []
8
+ }
@@ -0,0 +1,106 @@
1
+ import { componentNameValidationMixin } from '@descope-ui/common/components-mixins';
2
+ import { createBaseClass } from '@descope-ui/common/base-classes';
3
+ import { getComponentName } from '@descope-ui/common/components-helpers';
4
+ import { compose } from '@descope-ui/common/utils';
5
+
6
+ // NOTICE: This component registers with a DIFFERENT name than its file name
7
+ export const componentName = getComponentName('ponyhot');
8
+
9
+ const HP_PREFIX = 'dhp-';
10
+
11
+ const BaseClass = createBaseClass({
12
+ componentName,
13
+ baseSelector: '',
14
+ });
15
+
16
+ class RawHoneypot extends BaseClass {
17
+ get type() {
18
+ return this.getAttribute('type');
19
+ }
20
+
21
+ get value() {
22
+ return this.input?.value || '';
23
+ }
24
+
25
+ init() {
26
+ this.input = this.querySelector('input');
27
+
28
+ if (!this.input) return;
29
+
30
+ this.#setupTrapListeners();
31
+ this.#setupValueInterceptor();
32
+ this.#hideComponent();
33
+ }
34
+
35
+ #setupTrapListeners() {
36
+ const events = [
37
+ 'input',
38
+ 'change',
39
+ 'click',
40
+ 'keydown',
41
+ 'keyup',
42
+ 'mouseover',
43
+ 'mousedown',
44
+ ];
45
+
46
+ const fillHoneypot = (e) => {
47
+ this.input.value = `${HP_PREFIX}${e.type}`;
48
+
49
+ // Stop listening to all events after first trigger
50
+ events.forEach((event) => {
51
+ this.input.removeEventListener(event, fillHoneypot);
52
+ });
53
+ };
54
+
55
+ events.forEach((event) => {
56
+ this.input.addEventListener(event, fillHoneypot);
57
+ });
58
+ }
59
+
60
+ // Intercept direct value assignments to catch bots that bypass events
61
+ #setupValueInterceptor() {
62
+ const originalDescriptor = Object.getOwnPropertyDescriptor(
63
+ HTMLInputElement.prototype,
64
+ 'value',
65
+ );
66
+
67
+ Object.defineProperty(this.input, 'value', {
68
+ get() {
69
+ return originalDescriptor.get.call(this);
70
+ },
71
+ set(newValue) {
72
+ const currentValue = originalDescriptor.get.call(this);
73
+
74
+ // Only trap if the value is being set by external code (not our own trap)
75
+ if (
76
+ newValue &&
77
+ !newValue.startsWith(HP_PREFIX) &&
78
+ newValue !== currentValue
79
+ ) {
80
+ originalDescriptor.set.call(this, `${HP_PREFIX}direct-set`);
81
+ } else {
82
+ originalDescriptor.set.call(this, newValue);
83
+ }
84
+ },
85
+ configurable: true,
86
+ });
87
+ }
88
+
89
+ #hideComponent() {
90
+ const styles = {
91
+ position: 'absolute',
92
+ opacity: '0.01',
93
+ width: '1px',
94
+ height: '1px',
95
+ pointerEvents: 'none',
96
+ zIndex: '-1',
97
+ left: '-999999px',
98
+ };
99
+
100
+ Object.entries(styles).forEach(([key, value]) => {
101
+ this.style[key] = value;
102
+ });
103
+ }
104
+ }
105
+
106
+ export const HoneypotClass = compose(componentNameValidationMixin)(RawHoneypot);
@@ -0,0 +1,5 @@
1
+ import { componentName, HoneypotClass } from './HoneypotClass';
2
+
3
+ customElements.define(componentName, HoneypotClass);
4
+
5
+ export { HoneypotClass, componentName };
@@ -0,0 +1,40 @@
1
+ import { componentName } from '../src/component';
2
+
3
+ const Template = ({ name, type }) => `
4
+ <descope-ponyhot>
5
+ <input
6
+ name="${name || 'required-descope-validation'}"
7
+ type="${type || 'text'}"
8
+ autocomplete="off"
9
+ tabindex="-1"
10
+ aria-hidden="true"
11
+ data-1p-ignore
12
+ data-lpignore="true"
13
+ data-bwignore
14
+ data-form-type="other"
15
+ data-protonpass-ignore
16
+ />
17
+ </descope-ponyhot>
18
+ `;
19
+
20
+ export default {
21
+ component: componentName,
22
+ title: 'descope-honeypot',
23
+ parameters: {
24
+ panelPosition: 'right',
25
+ controls: { expanded: true },
26
+ },
27
+ argTypes: {
28
+ type: {
29
+ control: { type: 'select' },
30
+ options: ['text', 'email', 'password', 'address'],
31
+ },
32
+ },
33
+ };
34
+
35
+ export const Default = Template.bind({});
36
+
37
+ Default.args = {
38
+ name: 'text',
39
+ type: 'text',
40
+ };