diff --git a/apps/ssb/tf-app.js b/apps/ssb/tf-app.js
index ee30ffa9..bc1bf2fe 100644
--- a/apps/ssb/tf-app.js
+++ b/apps/ssb/tf-app.js
@@ -245,13 +245,16 @@ class TfElement extends LitElement {
if (confirm("Are you sure you want to create a new identity?")) {
await tfrpc.rpc.createIdentity();
this.ids = (await tfrpc.rpc.getIdentities()) || [];
+ if (this.ids && !this.whoami) {
+ this.whoami = this.ids[0];
+ }
}
}
render_id_picker() {
return html`
-
+
`;
}
@@ -293,7 +296,7 @@ class TfElement extends LitElement {
let users = this.users;
if (this.tab === 'news') {
return html`
- this.unread = []}>
+ this.unread = []}>
`;
} else if (this.tab === 'connections') {
return html`
@@ -325,7 +328,6 @@ class TfElement extends LitElement {
let self = this;
if (!this.loading && this.whoami && this.loaded !== this.whoami) {
- console.log(`starting loading ${this.whoami} ${this.loaded}`);
this.loading = true;
this.load().finally(function() {
self.loading = false;
@@ -355,4 +357,4 @@ class TfElement extends LitElement {
}
}
-customElements.define('tf-app', TfElement);
\ No newline at end of file
+customElements.define('tf-app', TfElement);
diff --git a/apps/ssb/tf-compose.js b/apps/ssb/tf-compose.js
index 7276d3d1..cd100dad 100644
--- a/apps/ssb/tf-compose.js
+++ b/apps/ssb/tf-compose.js
@@ -372,7 +372,7 @@ class TfComposeElement extends LitElement {
${Object.values(draft.mentions || {}).map(x => self.render_mention(x))}
${this.render_content_warning()}
${this.render_attach_app()}
-
+
${this.render_attach_app_button()}
@@ -381,4 +381,4 @@ class TfComposeElement extends LitElement {
}
}
-customElements.define('tf-compose', TfComposeElement);
\ No newline at end of file
+customElements.define('tf-compose', TfComposeElement);
diff --git a/apps/ssb/tf-id-picker.js b/apps/ssb/tf-id-picker.js
index 900eeb6c..1b9516cb 100644
--- a/apps/ssb/tf-id-picker.js
+++ b/apps/ssb/tf-id-picker.js
@@ -14,7 +14,6 @@ class TfIdentityPickerElement extends LitElement {
constructor() {
super();
- let self = this;
this.ids = [];
}
@@ -34,4 +33,4 @@ class TfIdentityPickerElement extends LitElement {
}
}
-customElements.define('tf-id-picker', TfIdentityPickerElement);
\ No newline at end of file
+customElements.define('tf-id-picker', TfIdentityPickerElement);
diff --git a/apps/ssb/tf-tab-news.js b/apps/ssb/tf-tab-news.js
index 6f4eab78..84f509bc 100644
--- a/apps/ssb/tf-tab-news.js
+++ b/apps/ssb/tf-tab-news.js
@@ -109,11 +109,11 @@ class TfTabNewsElement extends LitElement {
🏠Home
Welcome, !
-
+
${profile}
`;
}
}
-customElements.define('tf-tab-news', TfTabNewsElement);
\ No newline at end of file
+customElements.define('tf-tab-news', TfTabNewsElement);
diff --git a/core/client.js b/core/client.js
index db8bd117..fa662318 100644
--- a/core/client.js
+++ b/core/client.js
@@ -96,9 +96,9 @@ class TfNavigationElement extends LitElement {
render_login() {
if (this?.credentials?.session?.name) {
- return html`logout ${this.credentials.session.name}`;
+ return html`logout ${this.credentials.session.name}`;
} else {
- return html`login`;
+ return html`login`;
}
}
@@ -704,11 +704,13 @@ function api_requestPermission(permission, id) {
const k_options = [
{
+ id: 'allow',
text: '✅ Allow',
grant: ['allow once', 'allow'],
},
{
+ id: 'deny',
text: '❌ Deny',
grant: ['deny once', 'deny'],
},
@@ -719,6 +721,7 @@ function api_requestPermission(permission, id) {
for (let option of k_options) {
let button = document.createElement('button');
button.innerText = option.text;
+ button.id = option.id;
button.onclick = function() {
resolve(option.grant[check.checked ? 1 : 0]);
document.body.removeChild(outer);
diff --git a/src/main.c b/src/main.c
index f89624bf..ec210e12 100644
--- a/src/main.c
+++ b/src/main.c
@@ -353,16 +353,13 @@ static int _tf_run_task(const tf_run_args_t* args, int index)
}
tf_task_set_db_path(task, db_path);
tf_task_activate(task);
- if (args->ssb_port)
+ if (args->zip)
{
- if (args->zip)
- {
- tf_ssb_import_from_zip(tf_task_get_ssb(task), args->zip, "core", "apps");
- }
- else
- {
- tf_ssb_import(tf_task_get_ssb(task), "core", "apps");
- }
+ tf_ssb_import_from_zip(tf_task_get_ssb(task), args->zip, "core", "apps");
+ }
+ else
+ {
+ tf_ssb_import(tf_task_get_ssb(task), "core", "apps");
}
if (tf_task_execute(task, args->script))
{
diff --git a/src/ssb.c b/src/ssb.c
index 486517ec..d0e20933 100644
--- a/src/ssb.c
+++ b/src/ssb.c
@@ -239,6 +239,7 @@ typedef struct _tf_ssb_t
void* hitch_user_data;
tf_ssb_store_queue_t store_queue;
+ int ref_count;
} tf_ssb_t;
typedef struct _tf_ssb_connection_message_request_t
@@ -2278,7 +2279,8 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
ssb->broadcast_timer.data ||
ssb->broadcast_cleanup_timer.data ||
ssb->trace_timer.data ||
- ssb->server.data)
+ ssb->server.data ||
+ ssb->ref_count)
{
uv_run(ssb->loop, UV_RUN_ONCE);
}
@@ -2341,7 +2343,11 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
}
if (ssb->loop == &ssb->own_loop)
{
- uv_loop_close(ssb->loop);
+ int r = uv_loop_close(ssb->loop);
+ if (r != 0)
+ {
+ tf_printf("uv_loop_close: %s\n", uv_strerror(r));
+ }
}
if (ssb->own_context)
{
@@ -3568,3 +3574,13 @@ tf_ssb_store_queue_t* tf_ssb_get_store_queue(tf_ssb_t* ssb)
{
return &ssb->store_queue;
}
+
+void tf_ssb_ref(tf_ssb_t* ssb)
+{
+ ssb->ref_count++;
+}
+
+void tf_ssb_unref(tf_ssb_t* ssb)
+{
+ ssb->ref_count--;
+}
diff --git a/src/ssb.h b/src/ssb.h
index b0ab3013..670c2d5a 100644
--- a/src/ssb.h
+++ b/src/ssb.h
@@ -200,3 +200,6 @@ uint64_t tf_ssb_get_average_thread_time(tf_ssb_t* ssb);
void tf_ssb_set_hitch_callback(tf_ssb_t* ssb, void (*callback)(const char* name, uint64_t duration_ns, void* user_data), void* user_data);
tf_ssb_store_queue_t* tf_ssb_get_store_queue(tf_ssb_t* ssb);
+
+void tf_ssb_ref(tf_ssb_t* ssb);
+void tf_ssb_unref(tf_ssb_t* ssb);
diff --git a/src/ssb.rpc.c b/src/ssb.rpc.c
index d3724d17..e7549c24 100644
--- a/src/ssb.rpc.c
+++ b/src/ssb.rpc.c
@@ -1209,6 +1209,7 @@ static void _tf_ssb_rpc_delete_blobs_after_work(uv_work_t* work, int status)
{
delete_blobs_work_t* delete = work->data;
tf_ssb_record_thread_time(delete->ssb, (int64_t)delete->thread_id, delete->end_time - delete->start_time);
+ tf_ssb_unref(delete->ssb);
tf_free(delete);
}
@@ -1238,6 +1239,7 @@ static void _tf_ssb_rpc_start_delete_blobs(tf_ssb_t* ssb, int delay_ms)
*timer = (uv_timer_t) { .data = ssb };
uv_timer_init(tf_ssb_get_loop(ssb), timer);
uv_timer_start(timer, _tf_ssb_rpc_start_delete_timer, delay_ms, 0);
+ tf_ssb_ref(ssb);
}
void tf_ssb_rpc_register(tf_ssb_t* ssb)
diff --git a/tools/autotest.py b/tools/autotest.py
new file mode 100644
index 00000000..66786ecf
--- /dev/null
+++ b/tools/autotest.py
@@ -0,0 +1,58 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions
+from selenium.webdriver.support.ui import WebDriverWait
+
+import os
+import subprocess
+import time
+
+for path in ('out/selenium.sqlite', 'out/selenium.sqlite-shm', 'out/selenium.sqlite-wal'):
+ try:
+ os.unlink(path)
+ except:
+ pass
+tf = subprocess.Popen(['out/debug/tildefriends', 'run', '-d', 'out/selenium.sqlite', '-b', '0', '-p', '8888'])
+
+try:
+ options = webdriver.FirefoxOptions()
+ #options.add_argument('--headless')
+ driver = webdriver.Firefox(options = options)
+ driver.get('http://localhost:8888')
+ driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.LINK_TEXT, 'login').click()
+ driver.find_element(By.ID, 'register').click()
+ driver.find_element(By.ID, 'name').send_keys('test_user')
+ driver.find_element(By.ID, 'password').send_keys('test_password')
+ driver.find_element(By.ID, 'confirm').send_keys('test_password')
+ driver.find_element(By.ID, 'loginButton').click()
+
+ driver.switch_to.frame(driver.find_element(By.ID, 'document'))
+ WebDriverWait(driver, 30).until(expected_conditions.presence_of_element_located((By.LINK_TEXT, 'ssb')))
+ driver.find_element(By.LINK_TEXT, 'ssb').click()
+ driver.switch_to.default_content()
+
+ WebDriverWait(driver, 30).until(expected_conditions.presence_of_element_located((By.ID, 'document')))
+ driver.switch_to.frame(driver.find_element(By.ID, 'document'))
+ WebDriverWait(driver, 30).until(expected_conditions.presence_of_element_located((By.TAG_NAME, 'tf-app')))
+ tf_app = driver.find_element(By.TAG_NAME, 'tf-app').shadow_root
+ WebDriverWait(driver, 30).until(expected_conditions.element_to_be_clickable(tf_app.find_element(By.ID, 'create_identity')))
+ tf_app.find_element(By.ID, 'create_identity').click()
+ alert = WebDriverWait(driver, 30).until(expected_conditions.alert_is_present())
+ alert.accept()
+
+ driver.implicitly_wait(3)
+ #tf_app = driver.find_element(By.TAG_NAME, 'tf-app').shadow_root
+ #WebDriverWait(driver, 30).until(expected_conditions.visibility_of(tf_app.find_element(By.ID, 'tf-tab-news')))
+ tf_tab_news = tf_app.find_element(By.ID, 'tf-tab-news').shadow_root
+ tf_tab_news.find_element(By.ID, 'tf-compose').shadow_root.find_element(By.ID, 'edit').send_keys('Hello, world!')
+ tf_tab_news.find_element(By.ID, 'tf-compose').shadow_root.find_element(By.ID, 'submit').click()
+
+ driver.switch_to.default_content()
+ driver.find_element(By.ID, 'allow').click()
+
+ print('SUCCESS.')
+finally:
+ driver.close()
+ driver.quit()
+
+tf.terminate()